wake-up-neo.net

typ T von IEnumerable <T> abrufen

gibt es eine Möglichkeit, den Typ T von IEnumerable<T> durch Reflektion abzurufen?

z.B.

ich habe eine Variable IEnumerable<Child> info; Ich möchte den Typ des Kindes durch Reflexion abrufen

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

Also,

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

druckt System.String.

Siehe MSDN für Type.GetGenericArguments.

Edit: Ich glaube, das wird die Bedenken in den Kommentaren ansprechen:

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

Einige Objekte implementieren mehr als ein generisches IEnumerable, daher muss eine Aufzählung von ihnen zurückgegeben werden.

Edit: Obwohl ich sagen muss, ist es eine schreckliche Idee, dass eine Klasse IEnumerable<T> für mehrere T implementiert.

129
jason

Ich würde nur eine Erweiterungsmethode machen. Das funktionierte mit allem, was ich darauf warf. 

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

Ich hatte ein ähnliches Problem. Die ausgewählte Antwort funktioniert für tatsächliche Instanzen ..__ In meinem Fall hatte ich nur einen Typ (von einer PropertyInfo).

Die ausgewählte Antwort schlägt fehl, wenn der Typ selbst typeof(IEnumerable<T>) ist und keine Implementierung von IEnumerable<T> ist.

Für diesen Fall funktionieren die folgenden:

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;
}
21
Eli Algranti

Wenn Sie den IEnumerable<T> (über Generics) kennen, sollte typeof(T) funktionieren. Andernfalls (für object oder die nicht generische IEnumerable) überprüfen Sie die implementierten Schnittstellen:

        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);
19
Marc Gravell

Vielen Dank für die Diskussion. Ich habe es als Grundlage für die Lösung verwendet, die sich für alle Fälle eignet, die für mich von Interesse sind (IEnumerable, abgeleitete Klassen usw.). Ich möchte hier teilen, falls jemand es auch braucht:

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

Verwenden Sie einfach typeof(T)

EDIT: Oder verwenden Sie .GetType (). GetGenericParameter () für ein instantiiertes Objekt, wenn Sie nicht über T verfügen. 

2
rein

Eine Alternative für einfachere Situationen, in denen entweder IEnumerable<T> oder T verwendet wird. Beachten Sie die Verwendung von GenericTypeArguments anstelle von GetGenericArguments().

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

    return genericType;
} else {
    return inputType;
}
2
Rob Church

Dies ist eine Verbesserung der Lösung von Eli Algranti, da sie auch dort funktioniert, wo der IEnumerable<>-Typ sich auf einer beliebigen Ebene im Vererbungsbaum befindet.

Diese Lösung erhält den Elementtyp von einer beliebigen Type. Wenn der Typ kein IEnumerable<> ist, wird der übergebene Typ zurückgegeben. Verwenden Sie für Objekte GetType. Verwenden Sie für Typen typeof und rufen Sie dann diese Erweiterungsmethode für das Ergebnis auf.

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
Neo

Ich weiß, dass dies ein bisschen alt ist, aber ich glaube, dass diese Methode alle in den Kommentaren genannten Probleme und Herausforderungen abdecken wird. Dank an Eli Algranti für die Inspiration meiner Arbeit.

/// <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
dahall

typeof(IEnumerable<Foo>). GetGenericArguments()[0] gibt das erste generische Argument zurück - in diesem Fall typeof(Foo).

0

Hier ist meine nicht lesbare Version des Linq-Abfrageausdrucks.

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

Beachten Sie, dass die Methode auch nicht generische IEnumerable berücksichtigt und in diesem Fall object zurückgibt, da sie als Argument eine Type anstelle einer konkreten Instanz verwendet. Übrigens, für x steht für das Unbekannte , ich fand dieses Video interessant, obwohl es irrelevant ist ..

0
Ken Kin
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();
}

Dies ist eine rekursive Funktion, die zuerst die Liste der generischen Typen durchläuft, bis sie eine konkrete Typdefinition ohne innere generische Typen erhält.

Ich habe diese Methode mit folgendem Typ getestet: ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<IActionResult>>>>>>>>

die IActionResult zurückgeben sollte

0
Tyler Huskins

so mache ich das normalerweise (über Erweiterungsmethode):

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