existe-t-il un moyen de récupérer le type à T
partir IEnumerable<T>
de la réflexion?
par exemple
j'ai une IEnumerable<Child>
info variable ; je veux récupérer le type de l'enfant par réflexion
existe-t-il un moyen de récupérer le type à T
partir IEnumerable<T>
de la réflexion?
par exemple
j'ai une IEnumerable<Child>
info variable ; je veux récupérer le type de l'enfant par réflexion
Réponses:
IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0];
Ainsi,
IEnumerable<string> strings = new List<string>();
Console.WriteLine(strings.GetType().GetGenericArguments()[0]);
impressions System.String
.
Voir MSDN pour Type.GetGenericArguments
.
Edit: Je pense que cela 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 plusieurs génériques IEnumerable
, il est donc nécessaire d'en renvoyer une énumération.
Edit: Bien que, je dois dire, c'est une idée terrible pour une classe à implémenter IEnumerable<T>
pour plus d'un T
.
Je ferais juste une méthode d'extension. Cela a fonctionné avec tout ce que je lui ai lancé.
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 a PropertyInfo
).
La réponse sélectionnée échoue lorsque le type lui-même n'est typeof(IEnumerable<T>)
pas 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;
}
Type.GenericTypeArguments
- uniquement pour la version dotNet FrameWork> = 4.5. Sinon, utilisez Type.GetGenericArguments
plutôt.
Si vous connaissez le IEnumerable<T>
(via les génériques), alors cela typeof(T)
devrait fonctionner. Sinon (pour object
, ou le non générique IEnumerable
), 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);
Type type
paramètre plutôt qu'un object obj
paramètre: vous ne pouvez pas simplement remplacer obj.GetType()
par type
parce que si vous passez, typeof(IEnumerable<T>)
vous n'obtenez rien. Pour contourner cela, testez le type
lui - même pour voir s'il s'agit d'un générique de IEnumerable<>
, puis de ses interfaces.
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.). J'ai pensé que je devrais 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;
}
someCollection.GetType().GetInterface(typeof(IEnumerable<>).Name)?.GetGenericArguments()?.FirstOrDefault()
Une alternative pour les situations plus simples où ce sera soit une utilisation IEnumerable<T>
ou 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 qu'elle fonctionnera également là où le IEnumerable<>
type se trouve à n'importe quel niveau de l'arborescence d'héritage.
Cette solution obtiendra le type d'élément de any Type
. Si le type n'est pas an IEnumerable<>
, il renverra le type passé. 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 pense que cette méthode couvrira tous les problèmes et défis mentionnés dans les commentaires. Merci à Eli Algranti pour 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<>);
}
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 en profondeur d'abord dans la liste des types génériques jusqu'à ce qu'elle obtienne 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 revenir IActionResult
typeof(IEnumerable<Foo>)
. renverra le premier argument générique - dans ce cas .GetGenericArguments()
[0]
typeof(Foo)
Voici ma version illisible de l'expression de requête 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);
}
Notez que la méthode prend également IEnumerable
en compte le non générique , elle renvoie object
dans ce cas, car elle prend une Type
instance plutôt qu'une instance concrète comme argument. Au fait, pour x représente l'inconnu , j'ai trouvé cette vidéo insteresting, bien que ce ne soit pas pertinent.