web-dev-qa-db-fra.com

obtenir le type T de IEnumerable <T>

existe-t-il un moyen de récupérer le type T à partir de IEnumerable<T> par réflexion?

par exemple.

j'ai une variable IEnumerable<Child> info; je veux récupérer le type de l'enfant par réflexion

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

Ainsi,

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

imprime System.String.

Voir MSDN pour Type.GetGenericArguments.

Edit: Je pense que cela répondra aux préoccupations exprimées dans les commentaires:

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

Certains objets implémentent plusieurs variables génériques IEnumerable. Il est donc nécessaire de renvoyer une énumération de ces objets.

Edit: Bien que, je dois le dire, c’est une très mauvaise idée pour une classe d’implémenter IEnumerable<T> pour plus d’une T.

129
jason

Je ferais juste une méthode d'extension. Cela a fonctionné avec tout ce que j'ai jeté à elle. 

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

J'avais un problème similaire. La réponse sélectionnée fonctionne pour les instances réelles . Dans mon cas, je n'avais qu'un type (de PropertyInfo).

La réponse sélectionnée échoue lorsque le type lui-même est typeof(IEnumerable<T>) et non une implémentation de IEnumerable<T>.

Dans ce cas, les travaux suivants:

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

Si vous connaissez le IEnumerable<T> (via des génériques), alors typeof(T) devrait fonctionner. Sinon (pour object, ou le IEnumerable non générique), vérifiez les interfaces implémentées:

        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

Merci beaucoup pour la discussion. Je l'ai utilisé comme base pour la solution ci-dessous, qui convient à tous les cas qui m'intéressent (IEnumerable, classes dérivées, etc.) Je pensais que je devrais partager ici au cas où quelqu'un en aurait besoin aussi:

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

Il suffit d'utiliser typeof(T)

EDIT: Ou utilisez .GetType (). GetGenericParameter () sur un objet instancié si vous n'avez pas T. 

2
rein

Une alternative pour les situations plus simples où il s'agira soit d'un IEnumerable<T>, soit d'un T - notez l'utilisation de GenericTypeArguments au lieu de 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

Il s'agit d'une amélioration par rapport à la solution d'Eli Algranti en ce sens que cela fonctionne également lorsque le type IEnumerable<> est à n'importe quel niveau de l'arbre d'héritage.

Cette solution obtiendra le type d'élément de n'importe quelle Type. Si le type n'est pas un IEnumerable<>, il retournera le type transmis. Pour les objets, utilisez GetType. Pour les types, utilisez typeof, puis appelez cette méthode d'extension sur le résultat.

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

Je sais que c'est un peu vieux, mais je crois que cette méthode couvrira tous les problèmes et défis énoncés dans les commentaires. Nous remercions Eli Algranti d’avoir inspiré mon travail.

/// <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] retournera le premier argument générique - dans ce cas, typeof(Foo).

0
Daniel Brückner

Voici ma version d'expression de requête Linq illisible ..

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

Notez que la méthode prend également en compte les IEnumerable non génériques, elle renvoie object dans ce cas, car elle prend comme argument un Type plutôt qu'une instance concrète. A propos, pour x représente l'inconnu , j'ai trouvé cette vidéo insteresting, bien que ce ne soit pas pertinent.

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

Il s'agit d'une fonction récursive qui ira d'abord en profondeur dans la liste des types génériques jusqu'à obtenir une définition de type concrète sans types génériques internes.

J'ai testé cette méthode avec ce type: ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<IActionResult>>>>>>>>

qui devrait retourner IActionResult

0
Tyler Huskins

voici comment je le fais habituellement (via la méthode d'extension):

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