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
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
.
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);
}
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;
}
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);
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;
}
Il suffit d'utiliser typeof(T)
EDIT: Ou utilisez .GetType (). GetGenericParameter () sur un objet instancié si vous n'avez pas T.
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;
}
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;
}
}
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<>);
}
typeof(IEnumerable<Foo>)
. GetGenericArguments()
[0]
retournera le premier argument générique - dans ce cas, typeof(Foo)
.
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.
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
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];
}