2009-05-25 10 views
78

hay una manera de recuperar el tipo de TIEnumerable<T> través de la reflexión?conseguir tipo T de IEnumerable <T>

p. Ej.

tengo una variable IEnumerable<Child> información; quiero recuperar el tipo del niño a través de la reflexión

+1

¿En qué contexto? ¿Qué es esto IEnumerable ? ¿Es una instancia de objeto enviada como argumento? ¿O que? –

Respuesta

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

Thusly,

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

impresiones System.String.

Ver MSDN para Type.GetGenericArguments.

Editar: Creo que esto va a abordar las preocupaciones en los comentarios:

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

Algunos objetos implementan más de un genérico IEnumerable por lo que es necesario devolver una enumeración de ellos.

Editar: Aunque, tengo que decir, que es una idea terrible para una clase para implementar IEnumerable<T> para más de un T.

+0

Esto no funcionará: pruébalo con myEnumerable = new string [0]. –

+0

O, lo que es peor, escriba un método con rendimientos e intente llamar GetType a una variable creada con este método. Le dirá que no es un tipo genérico. Así que, básicamente, no hay una forma universal de obtener T dada una variable de instancia de tipo IEnumerable

+1

O intente con la clase MyClass: IEnumerable {}. Esta clase no tiene una interfaz genérica. –

0

typeof(IEnumerable<Foo>). GetGenericArguments()[0] devolverá el primer argumento genérico - en este caso typeof(Foo).

2

Sólo tiene que utilizar typeof(T)

EDIT: O utilice .GetType() GetGenericParameter() sobre un objeto instanciado si usted no tiene T.

+0

No siempre tiene T. – jason

+0

Es cierto, en cuyo caso puede usar .GetType(). Voy a modificar mi respuesta. – rein

+0

Como notaron otros comentaristas, esto no siempre funciona. – jason

16

Si conoce la IEnumerable<T> (a través de los genéricos.), entonces solo typeof(T) debería funcionar. De lo contrario (por object, o la no genérico IEnumerable), comprobar las interfaces implementadas:

 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

Algunos objetos implementan más de un IEnumerable genérico. – jason

+5

@Jason - y en esos casos, la pregunta de "encontrar la T" ya es una pregunta dudosa; No puedo hacer nada al respecto ... –

+0

Un pequeño error para cualquiera que intente usar esto con un parámetro 'Type type' en lugar de un parámetro' object obj': no puede simplemente reemplazar 'obj.GetType()' con 'tipo' porque si pasa' typeof (IEnumerable ) 'no obtiene nada. Para evitar esto, pruebe el 'type' en sí mismo para ver si es un genérico de' IEnumerable <> 'y luego sus interfaces. –

5

muchas gracias para la discusión. Lo usé como base para la solución a continuación, que funciona bien para todos los casos que me interesan (IEnumerable, clases derivadas, etc.). Pensé que debería compartir aquí por si alguien lo necesita también:

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

que acababa de hacer un método de extensión. Esto funcionó con todo lo que le arrojé.

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

No funcionará si su referencia de tiempo de compilación es solo de tipo objeto. –

2

Una alternativa para las situaciones más simples donde está bien va a ser un IEnumerable<T> o T - utilización nota de GenericTypeArguments en lugar de GetGenericArguments().

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

    return genericType; 
} else { 
    return inputType; 
} 
16

Tuve un problema similar. La respuesta seleccionada funciona para instancias reales. En mi caso solo tenía un tipo (de un PropertyInfo).

La respuesta seleccionada falla cuando el tipo en sí es typeof(IEnumerable<T>) no una implementación de IEnumerable<T>.

Para este caso las siguientes obras:

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

Considere derecho a voto hasta la respuesta y la pregunta seleccionada si usted encuentra este código útil.

0

Esta es una mejora en la solución de Eli Algranti ya que también funcionará cuando el tipo IEnumerable<> esté en cualquier nivel en el árbol de herencia.

Esta solución obtendrá el tipo de elemento de Type. Si el tipo no es un IEnumerable<>, devolverá el tipo pasado. Para objetos, use GetType. Para los tipos, use typeof, luego llame a este método de extensión en el resultado.

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