116 votes

obtention du type T à partir de IEnumerable<T>

Existe-t-il un moyen de récupérer le type T de IEnumerable<T> par la réflexion ?

par exemple

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

1 votes

Dans quel contexte ? Qu'est-ce que cet IEnumerable<T> ? Est-ce une instance d'objet envoyée comme argument ? Ou quoi ?

155voto

Jason Points 125291
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 plus d'un générique IEnumerable il est donc nécessaire de renvoyer une énumération de ceux-ci.

Edit : Bien que, je dois dire, c'est une idée terrible pour une classe d'implémenter IEnumerable<T> pour plus d'un T .

0 votes

Ou pire encore, écrivez une méthode avec des retours de rendement et essayez d'appeler GetType sur une variable créée avec cette méthode. Le système vous dira qu'il ne s'agit pas d'un type générique. Donc, fondamentalement, il n'y a pas de moyen universel d'obtenir T étant donné une variable d'instance de type IEnumerable<T>.

1 votes

Ou essayez avec la classe MyClass : IEnumerable<int> {}. Cette classe n'a pas d'interface générique.

1 votes

Pourquoi se contenter d'obtenir les arguments génériques, puis de récupérer le type dans son indexeur ? C'est juste demander un désastre, surtout quand le langage supporte typeof(T) comme @amsprich le suggère dans sa réponse, qui peut aussi être utilisé avec un type générique ou connu...

40voto

amsprich Points 111

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

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

8 votes

Cela ne fonctionnera pas si votre référence au moment de la compilation est simplement de type objet.

31voto

Eli Algranti Points 3284

J'ai eu 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 (issu d'une PropertyInfo ).

La réponse sélectionnée échoue lorsque le type lui-même est typeof(IEnumerable<T>) pas une mise en œuvre de IEnumerable<T> .

Dans ce cas, la méthode suivante fonctionne :

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

0 votes

Ça a sauvé ma journée. Dans mon cas, j'ai ajouté une instruction if séparée pour gérer les chaînes de caractères puisqu'elle implémente IEnumerable<char>.

0 votes

Type.GenericTypeArguments - uniquement pour dotNet FrameWork version >= 4.5. Sinon - utilisez Type.GetGenericArguments à la place.

21voto

Marc Gravell Points 482669

Si vous connaissez le IEnumerable<T> (via les génériques), alors il suffit de typeof(T) devrait fonctionner. Sinon (pour object ou le non générique IEnumerable ), vérifiez les interfaces mises en œuvre :

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

9voto

Bernardo Points 91

Merci beaucoup pour cette discussion. Je m'en suis servi comme base pour la solution ci-dessous, qui fonctionne bien pour tous les cas qui m'intéressent (IEnumerable, classes dérivées, etc). J'ai pensé que je devais la partager ici au cas où quelqu'un en aurait également besoin :

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

0 votes

Voici une ligne simple qui fait tout cela en utilisant l'opérateur conditionnel null : someCollection.GetType().GetInterface(typeof(IEnumerable<>).‌​Name)?.GetGenericArg‌​uments()?.FirstOrDef‌​ault()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X