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.