2009-05-25 10 views
78

jest sposób na odzyskanie typu T z IEnumerable<T> przez odbicie?uzyskanie typu T z IEnumerable <T>

np.

Mam zmienną IEnumerable<Child> info; Chcę odzyskać typ dziecka poprzez refleksję

+1

W jakim kontekście? Co to jest IEnumerable ? Czy jest to instancja obiektu wysłana jako argument? Albo co? –

Odpowiedz

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

thusly,

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

drukuje System.String.

Zobacz MSDN dla .

Edit: Wierzę, że to będzie dotyczyć problemów w komentarzach:

// 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]); 
} 

Niektóre obiekty realizować więcej niż jeden ogólny IEnumerable więc konieczne jest, aby powrócić wyliczenie z nich.

Edit: Chociaż, muszę powiedzieć, że to straszny pomysł na klasę wdrożyć IEnumerable<T> dla więcej niż jednego T.

+0

To nie zadziała: spróbuj za pomocą myEnumerable = new string [0]. –

+0

Albo jeszcze gorzej napisz metodę z deklaracjami zwrotu i spróbuj wywołać metodę GetType na zmiennej utworzonej za pomocą tej metody. Pokaże ci, że to nie jest wydarzenie rodzajowe. Więc zasadniczo nie ma uniwersalnego sposobu na otrzymanie T zmiennej instancji typu IEnumerable

+1

Lub spróbuj klasą MyClass: IEnumerable {}. Ta klasa nie ma ogólnego interfejsu. –

2

Wystarczy użyć typeof(T)

EDIT: Albo użyć .GetType() GetGenericParameter() na instancja obiektu, jeśli nie masz T.

+0

Nie zawsze masz T. – jason

+0

Prawda, w takim przypadku możesz użyć .GetType(). Zmienię moją odpowiedź. – rein

+0

Jak zauważyli inni komentatorzy, nie zawsze to działa. – jason

16

Jeśli znasz IEnumerable<T> (przez leki generyczne.), a następnie wystarczy typeof(T). W przeciwnym razie (na object lub non-generic IEnumerable), sprawdź interfejsy realizowane:

 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

Niektóre obiekty implementują więcej niż jeden ogólny IEnumerable. – jason

+5

@Jason - aw tych przypadkach kwestia "znajdź T" jest już wątpliwym pytaniem; Nie mogę nic na to poradzić ... –

+0

Jedna mała próba dla każdego, kto próbuje użyć tego z parametrem 'Type type' zamiast parametru' object obj': nie można po prostu zastąpić 'obj.GetType()' z 'type', ponieważ jeśli przejdziesz w' typeof (IEnumerable ) 'otrzymujesz nic. Aby to obejść, przetestuj samego 'type', aby zobaczyć, czy jest to typowy element' IEnumerable <> ', a następnie jego interfejsy. –

5

Dziękuję bardzo za dyskusję. Użyłem go jako podstawy dla poniższego rozwiązania, które sprawdza się we wszystkich przypadkach, które mnie interesują (IEnumerable, pochodne klasy itp.). Pomyślałem, że powinienem się z nami podzielić na wypadek, gdyby ktoś tego potrzebował:

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

Właśnie opracowałem metodę przedłużenia. To działało ze wszystkim, co na to rzuciłem.

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

Nie zadziała, jeśli odwołanie do czasu kompilacji jest tylko obiektem typu. –

2

Alternatywą dla prostszych sytuacjach, kiedy to albo będzie to IEnumerable<T> lub T - wykorzystanie nuta GenericTypeArguments zamiast GetGenericArguments().

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

    return genericType; 
} else { 
    return inputType; 
} 
16

Miałem podobny problem. Wybrana odpowiedź działa dla rzeczywistych instancji. W moim przypadku miałem tylko typ (od PropertyInfo).

Wybrana odpowiedź kończy się niepowodzeniem, gdy sam typ to typeof(IEnumerable<T>), a nie wersja.

w tym przypadku następujące prace:

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; 
} 

Zastanów się prawa głosu wybraną odpowiedź i pytanie, jeśli okaże się to kod przydatna.

0

Jest to udoskonalenie rozwiązania Eli Algrantiego, ponieważ działa również tam, gdzie typ IEnumerable<> jest na dowolnym poziomie w drzewie dziedziczenia.

To rozwiązanie otrzyma typ elementu z dowolnego Type. Jeśli typ nie jest typu IEnumerable<>, zwróci on przekazany typ. W przypadku obiektów użyj GetType. W przypadku typów należy użyć typeof, a następnie wywołać tę metodę rozszerzenia na wyniku.

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; 
    } 
} 
Powiązane problemy