2009-05-25 6 views
78

Y at-il un moyen de récupérer le type T de IEnumerable<T> par réflexion?obtenir le type T de IEnumerable <T>

par exemple.

J'ai une variable IEnumerable<Child> info; je veux récupérer le type d'enfant par la réflexion

+1

Dans quel contexte? Qu'est-ce que c'est IEnumerable ? Est-ce une instance d'objet envoyée en argument? Ou quoi? –

Répondre

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

Thusly,

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

impressions System.String.

Voir MSDN pour Type.GetGenericArguments.

Edit: Je crois que ce répondra aux préoccupations 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 IEnumerable générique il est donc nécessaire de retourner une énumération d'entre eux.

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

+0

Cela ne fonctionnera pas: Essayez-le avec myEnumerable = new string [0]. –

+0

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. Il vous dira que ce n'est pas un événement de type générique. Donc, fondamentalement, il n'y a pas de façon universelle d'obtenir T une variable d'instance de type IEnumerable

+1

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

0

typeof(IEnumerable<Foo>). GetGenericArguments()[0] renverra le premier argument générique - dans ce cas typeof(Foo).

2

Il suffit d'utiliser typeof(T)

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

+0

Vous n'avez pas toujours T. – jason

+0

True, auquel cas vous pouvez utiliser .GetType(). Je vais modifier ma réponse. – rein

+0

Comme d'autres commentateurs l'ont remarqué, cela ne fonctionne pas toujours. – jason

16

Si vous connaissez le IEnumerable<T> (via les génériques.), alors juste typeof(T) devrait fonctionner. Dans le cas contraire (pour object, ou IEnumerable non générique), vérifier 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); 
+3

Certains objets implémentent plus d'un IEnumerable générique. – jason

+5

@Jason - et dans ces cas, la question de "trouver le T" est déjà une question douteuse; Je ne peux rien faire à ce sujet ... –

+0

Un petit mot pour ceux qui essaient d'utiliser ceci avec un paramètre 'Type type' plutôt qu'un paramètre' object obj': vous ne pouvez pas simplement remplacer 'obj.GetType()' avec 'type' car si vous passez dans' typeof (IEnumerable ) 'vous n'obtenez rien. Pour contourner cela, testez le 'type' lui-même pour voir s'il est un générique de' IEnumerable <> 'et ensuite ses interfaces. –

5

Merci beaucoup pour la discussion. Je l'ai utilisé comme base pour la solution ci-dessous, qui fonctionne bien pour tous les cas qui m'intéressent (IEnumerable, classes dérivées, etc). Je pense 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; 
    } 
36

Je voudrais juste faire une méthode d'extension. Cela a fonctionné avec tout ce que je l'ai jeté.

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

Cela ne fonctionnera pas si votre référence de temps de compilation est juste de type objet. –

2

Une alternative pour les situations simples où il est soit va être un IEnumerable<T> ou T - notez l'usage 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; 
} 
16

J'ai eu un problème similaire. La réponse sélectionnée fonctionne pour les instances réelles. Dans mon cas, j'avais seulement un type (à partir d'un 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>.

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

droit de vote prendre en compte jusqu'à la réponse choisie et question si vous trouvez ce code utile.

0

Ceci est une amélioration par rapport à la solution d'Eli Algranti en ce sens que cela fonctionnera également là où le type IEnumerable<> est à n'importe quel niveau dans l'arbre d'héritage. Cette solution obtiendra le type d'élément à partir de Type. Si le type n'est pas un IEnumerable<>, il renvoie 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; 
    } 
} 
Questions connexes