Genel bir sınıf veya yöntemin bir üyesinden T türünü nasıl alabilirim?


675

Diyelim ki bir sınıf veya yöntemde genel bir üye var, bu yüzden:

public class Foo<T>
{
    public List<T> Bar { get; set; }

    public void Baz()
    {
        // get type of T
    }   
}

Ben sınıf örneğini zaman Tolur MyTypeObject1, bu yüzden sınıf genel liste özelliği vardır: List<MyTypeObject1>. Aynısı, genel olmayan bir sınıftaki genel bir yöntem için de geçerlidir:

public class Foo
{
    public void Bar<T>()
    {
        var baz = new List<T>();

        // get type of T
    }
}

Sınıfımın listesinin ne tür nesneler içerdiğini bilmek istiyorum. Yani list özellik denilen Barveya yerel değişken baz, ne tür içerir T?

Yapamıyorum Bar[0].GetType(), çünkü liste sıfır eleman içerebilir. Nasıl yapabilirim?

Yanıtlar:


695

Doğru anlarsam, listeniz kap sınıfının kendisiyle aynı tür parametresine sahiptir. Bu durumda, o zaman:

Type typeParameterType = typeof(T);

objectBir tür parametresi olarak sahip olduğunuz şanslı bir durumdaysanız, Marc'ın cevabına bakın .


4
Lol - evet, çok doğru; Ben OP sadece açtığını tahmin object, IListama bu çok iyi doğru cevabı olabilir - ya da benzer.
Marc Gravell

32
Ne kadar okunabilir typeofolduğunu seviyorum . T türünü bilmek istiyorsanız, sadece kullanın typeof(T):)
demoncodemonkey

2
Aslında typeof (Type) kullandım ve harika çalışıyor.
Anton

1
Yine de, typeof () öğesini genel bir parametreyle kullanamazsınız.
Reynevan

2
@Reynevan Tabii ki typeof()genel bir parametre ile kullanabilirsiniz . İşe yaramayacağı bir örnek var mı? Yoksa kafa karıştırıcı tip parametreleri ve referanslar mı?
Luaan

520

(Not: Ben tüm bildiklerini olduğunu tahmin ediyorum objectya IListveya benzer ve liste zamanında her tür olabileceği)

Bunun bir olduğunu biliyorsanız List<T>, o zaman:

Type type = abc.GetType().GetGenericArguments()[0];

Başka bir seçenek de dizinleyiciye bakmaktır:

Type type = abc.GetType().GetProperty("Item").PropertyType;

Yeni TypeInfo kullanma:

using System.Reflection;
// ...
var type = abc.GetType().GetTypeInfo().GenericTypeArguments[0];

1
Type = abc.GetType () yazın. GetGenericArguments () [0]; ==> Sınırların dışında dizi dizini ...
Patrick Desjardins

28
@Daok: o zaman bir Liste değil <T>
Marc Gravell

BindingList veya List ya da bir <T> içeren herhangi bir nesne için bir şeye ihtiyacınız var. Ne yapıyorum özel bir BindingListView <T> kullanın
Patrick Desjardins

1
BindingList <T> ile bir deneyin, bizim BindingListView <T> BindingList <T> miras ve her ikisi de ben senin seçenek her ikisini de denedim ve işe yaramaz. Yanlış bir şey yapabilirim ... ama bu çözüm Liste <T> türü için işe yarar ama diğer liste türü için değil düşünüyorum.
Patrick Desjardins

Type = abc.GetType (). GetProperty ("Öğe"). PropertyType; dönüş MyObject yerine BindingListView <MyObject> ...
Patrick Desjardins

49

Aşağıdaki uzantı yöntemiyle yansıma olmadan uzaklaşabilirsiniz:

public static Type GetListType<T>(this List<T> _)
{
    return typeof(T);
}

Veya daha genel:

public static Type GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return typeof(T);
}

Kullanımı:

List<string>        list    = new List<string> { "a", "b", "c" };
IEnumerable<string> strings = list;
IEnumerable<object> objects = list;

Type listType    = list.GetListType();           // string
Type stringsType = strings.GetEnumeratedType();  // string
Type objectsType = objects.GetEnumeratedType();  // BEWARE: object

10
Bu yalnızca Tderleme zamanında türünü biliyorsanız faydalıdır . Bu durumda, gerçekten herhangi bir koda ihtiyacınız yoktur.
özyinelemeli

'varsayılana dön (T);'
fantezisi

1
@recursive: Anonim türden bir listeyle çalışıyorsanız kullanışlıdır.
JJJ

Cevabınızı okumadan önce aynı şeyi yaptım ama benimkini ItemType
aramıştım

31

Deneyin

list.GetType().GetGenericArguments()

7
yeni Liste <int> () .GetType (). GetGenericArguments () burada System.Int32 ile giriş olarak System.Type [1] değerini döndürür
Rauhotz

@Rauhotz GetGenericArgumentsyöntemi Type, daha sonra ihtiyacınız olan Genel Tür'ün konumunu ayrıştırmanız gereken bir Array nesnesi döndürür . Gibi Type<TKey, TValue>: tip GetGenericArguments()[0]almak TKeyve tip GetGenericArguments()[1]almak için gerekirTValue
GoldBishop

14

Bu benim için iş. MyList'in bilinmeyen bir liste olduğu yerler.

IEnumerable myEnum = myList as IEnumerable;
Type entryType = myEnum.AsQueryable().ElementType;

1
Bir tür argüman (yani <T>) gerektirdiği bir hata alıyorum
Joseph Humfrey 21:15

Joseph ve diğerleri, System.Collections'da bulunan hatadan kurtulmak için.
user2334883

1
Benim için sadece ikinci hatta ihtiyaç var. A Listzaten bir uygulaması IEnumerable, bu yüzden oyuncular hiçbir şey eklemiyor gibi görünüyor. Ama teşekkürler, bu iyi bir çözüm.
pipedreambomb

10

Tüm Type değişkenine ihtiyacınız yoksa ve sadece türü kontrol etmek istiyorsanız, kolayca bir temp değişkeni oluşturabilirsiniz ve kullanım operatördür.

T checkType = default(T);

if (checkType is MyClass)
{}

9

Bunu genel liste dönüş türü için kullanabilirsiniz:

public string ListType<T>(T value)
{
    var valueType = value.GetType().GenericTypeArguments[0].FullName;
    return valueType;
}

8

Bunu düşünün: 20 tür listesini aynı şekilde vermek için kullanıyorum:

private void Generate<T>()
{
    T item = (T)Activator.CreateInstance(typeof(T));

    ((T)item as DemomigrItemList).Initialize();

    Type type = ((T)item as DemomigrItemList).AsEnumerable().FirstOrDefault().GetType();
    if (type == null) return;
    if (type != typeof(account)) //account is listitem in List<account>
    {
        ((T)item as DemomigrItemList).CreateCSV(type);
    }
}

1
T, gerçek eklenen nesnelerin soyut bir süper sınıfı ise bu işe yaramaz. Bahsetmiyorum, sadece new T();aynı şeyi yapardık (T)Activator.CreateInstance(typeof(T));. where T : new()Sınıf / işlev tanımına eklemenizi gerektirir , ancak nesne yapmak istiyorsanız , yine de yapılmalıdır.
Nyerguds

Ayrıca, potansiyel bir boş başvuru istisnasıyla sonuçlanan GetTypebir FirstOrDefaultgirdiyi çağırıyorsunuz . En az bir ürün iade edeceğinden eminseniz, bunun Firstyerine neden kullanmıyorsunuz ?
Mathias Lykkegaard Lorenzen

6

Ben benzer bir şey başarmak için bu uzantı yöntemini kullanın:

public static string GetFriendlyTypeName(this Type t)
{
    var typeName = t.Name.StripStartingWith("`");
    var genericArgs = t.GetGenericArguments();
    if (genericArgs.Length > 0)
    {
        typeName += "<";
        foreach (var genericArg in genericArgs)
        {
            typeName += genericArg.GetFriendlyTypeName() + ", ";
        }
        typeName = typeName.TrimEnd(',', ' ') + ">";
    }
    return typeName;
}

public static string StripStartingWith(this string s, string stripAfter)
{
    if (s == null)
    {
        return null;
    }
    var indexOf = s.IndexOf(stripAfter, StringComparison.Ordinal);
    if (indexOf > -1)
    {
        return s.Substring(0, indexOf);
    }
    return s;
}

Bu şekilde kullanırsınız:

[TestMethod]
public void GetFriendlyTypeName_ShouldHandleReallyComplexTypes()
{
    typeof(Dictionary<string, Dictionary<string, object>>).GetFriendlyTypeName()
        .ShouldEqual("Dictionary<String, Dictionary<String, Object>>");
}

Bu aradığınız şey değil, ancak ilgili tekniklerin gösterilmesinde yardımcı oluyor.


Merhaba, Cevabınız için teşekkürler, "StripStartingWith" uzantısını da ekleyebilir misiniz?
Cedric Arnould

1
@CedricArnould - Eklendi.
Ken Smith

5

GetGenericArgument()Yöntemi (kimin sınıfı genel sınıftır örneğinin Temel Türü üzerinde ayarlanmış olması gerekir myClass<T>). Aksi takdirde, bir tür döndürür [0]

Misal:

Myclass<T> instance = new Myclass<T>();
Type[] listTypes = typeof(instance).BaseType.GetGenericArguments();

5

IEnumerable <T> uygulayan herhangi bir koleksiyon türünden "T" türünü aşağıdakilerle alabilirsiniz:

public static Type GetCollectionItemType(Type collectionType)
{
    var types = collectionType.GetInterfaces()
        .Where(x => x.IsGenericType 
            && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))
        .ToArray();
    // Only support collections that implement IEnumerable<T> once.
    return types.Length == 1 ? types[0].GetGenericArguments()[0] : null;
}

IEnumerable <T> öğesini iki kez uygulayan toplama türlerini desteklemediğini unutmayın;

public class WierdCustomType : IEnumerable<int>, IEnumerable<string> { ... }

Sanırım bunu desteklemek için gerekirse bir dizi türü dönebilirsiniz ...

Ayrıca, bunu çok fazla yapıyorsanız (örneğin bir döngüde) toplama türü başına sonucu önbelleğe almak isteyebilirsiniz.


1
public bool IsCollection<T>(T value){
  var valueType = value.GetType();
  return valueType.IsArray() || typeof(IEnumerable<object>).IsAssignableFrom(valueType) || typeof(IEnumerable<T>).IsAssignableFrom(valuetype);
}

1
Bu, türün liste-y türünde bir şey olup olmadığı sorusunu ele alıyor gibi görünüyor, ancak soru, Liste olarak bilinen bir türün hangi genel tür parametresinin önceden başlatıldığını nasıl belirleyeceğidir.
Nathan Tuggy

1

3dGrabber'in çözümünü kullanarak:

public static T GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return default(T);
}

//and now 

var list = new Dictionary<string, int>();
var stronglyTypedVar = list.GetEnumeratedType();

0

Bir mülkün temelini oluşturan türü bilmek istiyorsanız şunu deneyin:

propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]

0

Ben böyle yaptım

internal static Type GetElementType(this Type type)
{
        //use type.GenericTypeArguments if exist 
        if (type.GenericTypeArguments.Any())
         return type.GenericTypeArguments.First();

         return type.GetRuntimeProperty("Item").PropertyType);
}

O zaman böyle söyle

var item = Activator.CreateInstance(iListType.GetElementType());

VEYA

var item = Activator.CreateInstance(Bar.GetType().GetElementType());

-9

Tür:

type = list.AsEnumerable().SingleOrDefault().GetType();

1
Listenin içinde sınamak için hiçbir öğesi yoksa, bu bir NullReferenceException özel durumu oluşturur.
rossisdead

1
SingleOrDefault()ayrıca InvalidOperationExceptioniki veya daha fazla eleman olduğunda atar .
devgeezer

\ @Rossisdead ve \ @devgeezer tarafından doğru şekilde belirtildiği gibi bu cevap yanlış.
Oliver
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.