¿hay una manera de recuperar el tipo T
de IEnumerable<T>
medio de la reflexión?
p.ej
tengo una IEnumerable<Child>
información variable ; quiero recuperar el tipo de niño a través de la reflexión
¿hay una manera de recuperar el tipo T
de IEnumerable<T>
medio de la reflexión?
p.ej
tengo una IEnumerable<Child>
información variable ; quiero recuperar el tipo de niño a través de la reflexión
Respuestas:
IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0];
Por lo tanto,
IEnumerable<string> strings = new List<string>();
Console.WriteLine(strings.GetType().GetGenericArguments()[0]);
impresiones System.String
.
Consulte MSDN para Type.GetGenericArguments
.
Editar: creo que esto 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, debo decir, es una idea terrible que una clase implemente IEnumerable<T>
para más de uno T
.
Solo haría un método de extensión. Esto funcionó con todo lo que le lancé.
public static Type GetItemType<T>(this IEnumerable<T> enumerable)
{
return typeof(T);
}
Tuve un problema similar. La respuesta seleccionada funciona para instancias reales. En mi caso solo tenía un tipo (de a PropertyInfo
).
La respuesta seleccionada falla cuando el tipo en sí typeof(IEnumerable<T>)
no es una implementación de IEnumerable<T>
.
Para este caso funciona lo siguiente:
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;
}
Type.GenericTypeArguments
- solo para dotNet FrameWork versión> = 4.5. De lo contrario, utilice Type.GetGenericArguments
en su lugar.
Si conoce IEnumerable<T>
(a través de genéricos), entonces typeof(T)
debería funcionar. De lo contrario (para object
, o el no genérico IEnumerable
), verifique 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);
Type type
parámetro en lugar de un object obj
parámetro: no puede simplemente reemplazarobj.GetType()
con type
porque si pasa, typeof(IEnumerable<T>)
no obtiene nada. Para solucionar esto, pruebe el type
propio para ver si es un genérico de IEnumerable<>
y luego sus interfaces.
Muchas gracias por 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í en caso de que alguien lo necesite también:
Type GetItemType(object someCollection)
{
var type = someCollection.GetType();
var ienum = type.GetInterface(typeof(IEnumerable<>).Name);
return ienum != null
? ienum.GetGenericArguments()[0]
: null;
}
someCollection.GetType().GetInterface(typeof(IEnumerable<>).Name)?.GetGenericArguments()?.FirstOrDefault()
Una alternativa para situaciones más simples en las que se utilizará una IEnumerable<T>
o una T
nota en GenericTypeArguments
lugar de GetGenericArguments()
.
Type inputType = o.GetType();
Type genericType;
if ((inputType.Name.StartsWith("IEnumerable"))
&& ((genericType = inputType.GenericTypeArguments.FirstOrDefault()) != null)) {
return genericType;
} else {
return inputType;
}
Esta es una mejora de la solución de Eli Algranti, ya que también funcionará cuando el IEnumerable<>
tipo esté en cualquier nivel del árbol de herencia.
Esta solución obtendrá el tipo de elemento de any Type
. Si el tipo no es unIEnumerable<>
, devolverá el tipo pasado. Para objetos, use GetType
. Para 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;
}
}
Sé que esto es un poco antiguo, pero creo que este método cubrirá todos los problemas y desafíos indicados en los comentarios. Gracias a Eli Algranti por inspirar mi trabajo.
/// <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<>);
}
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();
}
Esta es una función recursiva que profundizará primero en la lista de tipos genéricos hasta que obtenga una definición de tipo concreta sin tipos genéricos internos.
Probé este método con este tipo:
ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<IActionResult>>>>>>>>
que debería volver IActionResult
typeof(IEnumerable<Foo>)
. devolverá el primer argumento genérico, en este caso .GetGenericArguments()
[0]
typeof(Foo)
Aquí está mi versión ilegible de expresión de consulta Linq ...
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);
}
Tenga en cuenta que el método también tiene IEnumerable
en cuenta lo no genérico , object
en este caso devuelve , porque toma una Type
instancia en lugar de una concreta como argumento. Por cierto, ya que x representa lo desconocido , este video me pareció interesante, aunque irrelevante.