IEnumerable <T> 'den T türü alma


106

Yazıyı derinlemesine düşünerek Telde etmenin bir yolu var IEnumerable<T>mı?

Örneğin

değişken bir bilgim var IEnumerable<Child>; Düşünme yoluyla Çocuğun türünü geri almak istiyorum


1
Hangi bağlamda? Bu IEnumerable <T> nedir? Argüman olarak gönderilen bir nesne örneği mi? Ya da ne?
Mehrdad Afshari

Yanıtlar:


142
IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0]; 

Böylece,

IEnumerable<string> strings = new List<string>();
Console.WriteLine(strings.GetType().GetGenericArguments()[0]);

baskılar System.String.

Şunun için MSDN'ye bakınType.GetGenericArguments .

Düzenleme: Bunun yorumlardaki endişeleri gidereceğine inanıyorum:

// returns an enumeration of T where o : IEnumerable<T>
public IEnumerable<Type> GetGenericIEnumerables(object o) {
    return o.GetType()
            .GetInterfaces()
            .Where(t => t.IsGenericType
                && t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            .Select(t => t.GetGenericArguments()[0]);
}

Bazı nesneler birden fazla jenerik uygular IEnumerable, bu nedenle bunların bir numaralandırmasını döndürmek gerekir.

Düzenleme: Yine de söylemeliyim ki, bir sınıfın IEnumerable<T>birden fazlası için uygulaması korkunç bir fikir T.


Daha da kötüsü, getiri getirileri olan bir yöntem yazın ve bu yöntemle oluşturulan bir değişken üzerinde GetType'ı çağırmayı deneyin. Size bunun genel bir tip olmadığını söyleyecektir. Yani temelde, IEnumerable <T> türünde bir örnek değişkeni verildiğinde T'yi elde etmenin evrensel bir yolu yoktur
Darin Dimitrov

1
Veya Sınıfım: IEnumerable <int> {} sınıfını deneyin. Bu sınıfın genel bir arayüzü yoktur.
Stefan Steinegger

1
Neden biri genel argümanları almaya ve ardından türü indeksleyiciden almaya başvursun? Bu sadece felaket sormak, özellikle de @ amsprich'in cevabında önerdiği gibi typeof (T) 'yi desteklediğinde, bu aynı zamanda genel veya bilinen bir türle de kullanılabilir ...
Robert Petz

Bu, linq sorguları ile kullanıldığında sefil bir şekilde başarısız olur - WhereSelectEnumerableIterator'ın ilk genel argümanı değildir . Arayüzün kendisinin değil, temeldeki nesnenin genel argümanını alıyorsunuz.
Pxtl

myEnumerable.GetType (). GetGenericArguments () [0] size ad alanı.classname'yi söyleyen FullName özelliğini verir. Yalnızca sınıf adını arıyorsanız, myEnumerable.GetType () kullanın. GetGenericArguments () [0] .Name
user5534263

38

Ben sadece bir uzatma yöntemi yapardım. Bu, ona attığım her şeyle çalıştı.

public static Type GetItemType<T>(this IEnumerable<T> enumerable)
{
    return typeof(T);
}

6
Derleme zamanı referansınız sadece nesne türündeyse çalışmaz.
Stijn Van Antwerpen

27

Benzer bir problemim vardı. Seçilen cevap, gerçek durumlar için işe yarar. Benim durumumda sadece bir tipim vardı (birPropertyInfo ).

Tipin kendisi typeof(IEnumerable<T>)bir uygulaması olmadığında seçilen cevap başarısız olurIEnumerable<T> .

Bu durum için aşağıdakiler çalışır:

public static Type GetAnyElementType(Type type)
{
   // Type is Array
   // short-circuit if you expect lots of arrays 
   if (type.IsArray)
      return type.GetElementType();

   // type is IEnumerable<T>;
   if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof (IEnumerable<>))
      return type.GetGenericArguments()[0];

   // type implements/extends IEnumerable<T>;
   var enumType = type.GetInterfaces()
                           .Where(t => t.IsGenericType && 
                                  t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                           .Select(t => t.GenericTypeArguments[0]).FirstOrDefault();
   return enumType ?? type;
}

Günümü kurtardım. Benim durumum için, dizeleri işlemek için ayrı bir if ifadesi ekledim çünkü IEnumerable <char>
Edmund P Charumbira

Type.GenericTypeArguments- yalnızca dotNet FrameWork sürümü> = 4.5 için. Aksi takdirde - Type.GetGenericArgumentsonun yerine kullanın.
Кое Кто

20

IEnumerable<T>(Jenerikler yoluyla) biliyorsanız , o zaman typeof(T)çalışmalısınız. Aksi takdirde (için objectveya genel olmayan IEnumerable), uygulanan arayüzleri kontrol edin:

        object obj = new string[] { "abc", "def" };
        Type type = null;
        foreach (Type iType in obj.GetType().GetInterfaces())
        {
            if (iType.IsGenericType && iType.GetGenericTypeDefinition()
                == typeof(IEnumerable<>))
            {
                type = iType.GetGenericArguments()[0];
                break;
            }
        }
        if (type != null) Console.WriteLine(type);

3
Bazı nesneler birden fazla genel IEnumerable uygular.
jason

5
@ Jason - ve bu durumlarda, "T'yi bul" sorusu zaten şüpheli bir sorudur; Bu konuda hiçbir şey yapamam ...
Marc Gravell

Bunu bir Type typeparametre yerine bir parametreyle kullanmaya çalışan herkes için küçük bir aldatma object obj: obj.GetType()ile değiştiremezsiniz , typeçünkü typeof(IEnumerable<T>)iletirseniz hiçbir şey alamazsınız. Bunu typeaşmak için, genel olup olmadığını görmek için kendini IEnumerable<>ve sonra arayüzlerini test edin .
Ian Mercer

8

Tartışma için çok teşekkür ederim. Aşağıdaki çözümün temeli olarak kullandım, bu beni ilgilendiren tüm durumlar için iyi çalışıyor (IEnumerable, türetilmiş sınıflar, vb.). Ayrıca birinin ihtiyacı olursa burada paylaşmam gerektiğini düşündüm:

  Type GetItemType(object someCollection)
  {
    var type = someCollection.GetType();
    var ienum = type.GetInterface(typeof(IEnumerable<>).Name);
    return ienum != null
      ? ienum.GetGenericArguments()[0]
      : null;
  }

İşte tüm bunları sıfır koşullu operatör kullanarak yapan someCollection.GetType().GetInterface(typeof(IEnumerable<>).Name)?.GetGenericArguments()?.FirstOrDefault()
Mass Dot Net

2

Sadece kullan typeof(T)

DÜZENLEME: Veya T'ye sahip değilseniz, başlatılmış bir nesnede .GetType (). GetGenericParameter () kullanın.


Her zaman
T.'ye

Doğru, bu durumda .GetType () kullanabilirsiniz. Cevabımı değiştireceğim.
dizginler

2

Ya bir IEnumerable<T>ya da T- GenericTypeArgumentsyerine kullanılacağı daha basit durumlar için bir alternatif GetGenericArguments().

Type inputType = o.GetType();
Type genericType;
if ((inputType.Name.StartsWith("IEnumerable"))
    && ((genericType = inputType.GenericTypeArguments.FirstOrDefault()) != null)) {

    return genericType;
} else {
    return inputType;
}

1

Bu, Eli Algranti'nin çözümünde bir gelişmedir çünkü aynı zamanda IEnumerable<> türün miras ağacında herhangi bir düzeyde .

Bu çözüm, eleman türünü herhangi birinden alacaktır Type. Tür bir değilse IEnumerable<>, aktarılan türü döndürür. Nesneler için kullanın GetType. Türler için kullanın typeof, ardından sonuçta bu uzantı yöntemini çağırın.

public static Type GetGenericElementType(this Type type)
{
    // Short-circuit for Array types
    if (typeof(Array).IsAssignableFrom(type))
    {
        return type.GetElementType();
    }

    while (true)
    {
        // Type is IEnumerable<T>
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
        {
            return type.GetGenericArguments().First();
        }

        // Type implements/extends IEnumerable<T>
        Type elementType = (from subType in type.GetInterfaces()
            let retType = subType.GetGenericElementType()
            where retType != subType
            select retType).FirstOrDefault();

        if (elementType != null)
        {
            return elementType;
        }

        if (type.BaseType == null)
        {
            return type;
        }

        type = type.BaseType;
    }
}

1

Bunun biraz eski olduğunu biliyorum, ancak bu yöntemin yorumlarda belirtilen tüm sorunları ve zorlukları kapsayacağına inanıyorum. İşime ilham verdiği için Eli Algranti'ye teşekkür ederim.

/// <summary>Finds the type of the element of a type. Returns null if this type does not enumerate.</summary>
/// <param name="type">The type to check.</param>
/// <returns>The element type, if found; otherwise, <see langword="null"/>.</returns>
public static Type FindElementType(this Type type)
{
   if (type.IsArray)
      return type.GetElementType();

   // type is IEnumerable<T>;
   if (ImplIEnumT(type))
      return type.GetGenericArguments().First();

   // type implements/extends IEnumerable<T>;
   var enumType = type.GetInterfaces().Where(ImplIEnumT).Select(t => t.GetGenericArguments().First()).FirstOrDefault();
   if (enumType != null)
      return enumType;

   // type is IEnumerable
   if (IsIEnum(type) || type.GetInterfaces().Any(IsIEnum))
      return typeof(object);

   return null;

   bool IsIEnum(Type t) => t == typeof(System.Collections.IEnumerable);
   bool ImplIEnumT(Type t) => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>);
}

1
public static Type GetInnerGenericType(this Type type)
{
  // Attempt to get the inner generic type
  Type innerType = type.GetGenericArguments().FirstOrDefault();

  // Recursively call this function until no inner type is found
  return innerType is null ? type : innerType.GetInnerGenericType();
}

Bu özyinelemeli bir işlevdir ve iç genel tür içermeyen somut bir tür tanımı elde edene kadar genel türler listesinin derinliklerine iner.

Bu yöntemi şu türle test ettim: ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<IActionResult>>>>>>>>

hangisi geri dönmeli IActionResult



0

Ben genellikle böyle yaparım (uzatma yöntemiyle):

public static Type GetIEnumerableUnderlyingType<T>(this T iEnumerable)
    {
        return typeof(T).GetTypeInfo().GetGenericArguments()[(typeof(T)).GetTypeInfo().GetGenericArguments().Length - 1];
    }

0

İşte okunamayan Linq sorgu ifade sürümüm ..

public static Type GetEnumerableType(this Type t) {
    return !typeof(IEnumerable).IsAssignableFrom(t) ? null : (
    from it in (new[] { t }).Concat(t.GetInterfaces())
    where it.IsGenericType
    where typeof(IEnumerable<>)==it.GetGenericTypeDefinition()
    from x in it.GetGenericArguments() // x represents the unknown
    let b = it.IsConstructedGenericType // b stand for boolean
    select b ? x : x.BaseType).FirstOrDefault()??typeof(object);
}

Yöntemin jenerik olmayanları da IEnumerablehesaba kattığına dikkat edin object, bu durumda geri döner , çünkü Typeargüman olarak somut değil de bir örnek alır . Bu arada, x bilinmeyeni temsil ettiği için , alakasız olmasına rağmen bu videoyu ilginç buldum .

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.