czy jest jakiś sposób, aby odzyskać typ T
od IEnumerable<T>
poprzez odbicie?
na przykład
mam zmienne IEnumerable<Child>
informacje; chcę odzyskać typ dziecka poprzez refleksję
czy jest jakiś sposób, aby odzyskać typ T
od IEnumerable<T>
poprzez odbicie?
na przykład
mam zmienne IEnumerable<Child>
informacje; chcę odzyskać typ dziecka poprzez refleksję
Odpowiedzi:
IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0];
W ten sposób
IEnumerable<string> strings = new List<string>();
Console.WriteLine(strings.GetType().GetGenericArguments()[0]);
wydruki System.String
.
Zobacz MSDN dla Type.GetGenericArguments
.
Edycja: wierzę, że to rozwiąże obawy w komentarzach:
// 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]);
}
Niektóre obiekty implementują więcej niż jeden rodzaj, IEnumerable
więc konieczne jest zwrócenie ich wyliczenia.
Edycja: Chociaż, muszę powiedzieć, to okropny pomysł, aby zaimplementować klasę IEnumerable<T>
dla więcej niż jednej T
.
Zrobiłbym tylko metodę rozszerzenia. To działało ze wszystkim, co w to rzuciłem.
public static Type GetItemType<T>(this IEnumerable<T> enumerable)
{
return typeof(T);
}
Miałem podobny problem. Wybrana odpowiedź działa w rzeczywistych instancjach. W moim przypadku miałem tylko typ (od aPropertyInfo
).
Wybrana odpowiedź nie powiedzie się, jeśli sam typ typeof(IEnumerable<T>)
nie jest implementacją IEnumerable<T>
.
W tym przypadku działa:
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
- tylko dla dotNet FrameWork w wersji> = 4.5. W przeciwnym razie - użyj Type.GetGenericArguments
zamiast tego.
Jeśli znasz IEnumerable<T>
(przez generics), to typeof(T)
powinno działać. W przeciwnym razie (dla object
lub nieogólne IEnumerable
) sprawdź zaimplementowane interfejsy:
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
parametrem, a nie z object obj
parametrem: nie możesz po prostu zastąpić obj.GetType()
go, type
ponieważ jeśli przekażesz typeof(IEnumerable<T>)
, nic nie otrzymasz. Aby to obejść, przetestuj type
samą siebie, aby zobaczyć, czy jest to generyczna, IEnumerable<>
a następnie jej interfejsy.
Bardzo dziękuję za rozmowę. Użyłem go jako podstawy do poniższego rozwiązania, które działa dobrze we wszystkich interesujących mnie przypadkach (IEnumerable, klasy pochodne itp.). Pomyślałem, że powinienem podzielić się tutaj na wypadek, gdyby ktoś również tego potrzebował:
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()
Alternatywa dla prostszych sytuacji, w których będzie to użycie IEnumerable<T>
lub T
- uwaga GenericTypeArguments
zamiast zamiast GetGenericArguments()
.
Type inputType = o.GetType();
Type genericType;
if ((inputType.Name.StartsWith("IEnumerable"))
&& ((genericType = inputType.GenericTypeArguments.FirstOrDefault()) != null)) {
return genericType;
} else {
return inputType;
}
Jest to ulepszenie rozwiązania Eli Algranti, ponieważ będzie działać również tam, gdzie IEnumerable<>
typ znajduje się na dowolnym poziomie w drzewie dziedziczenia.
To rozwiązanie pozwoli uzyskać typ elementu z dowolnego Type
. Jeśli typ nie jest typu IEnumerable<>
, zwróci przekazany typ. W przypadku obiektów użyj GetType
. W przypadku typów użyj typeof
, a następnie wywołaj tę metodę rozszerzenia w wyniku.
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;
}
}
Wiem, że to trochę stare, ale uważam, że ta metoda obejmie wszystkie problemy i wyzwania wymienione w komentarzach. Podziękowania dla Eli Algranti za inspirację do mojej pracy.
/// <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();
}
Jest to funkcja rekurencyjna, która przejdzie najpierw w dół listy typów ogólnych, aż uzyska definicję konkretnego typu bez wewnętrznych typów ogólnych.
Testowałem tę metodę z tym typem:
ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<IActionResult>>>>>>>>
który powinien wrócić IActionResult
typeof(IEnumerable<Foo>)
. zwróci pierwszy argument ogólny - w tym przypadku .GetGenericArguments()
[0]
typeof(Foo)
Oto moja nieczytelna wersja wyrażenia zapytania 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);
}
Zauważ, że metoda bierze również IEnumerable
pod uwagę nieogólne, zwraca object
w tym przypadku, ponieważ Type
jako argument przyjmuje raczej instancję niż konkretną. Nawiasem mówiąc, ponieważ x reprezentuje nieznane , uznałem ten film za interesujący , chociaż nie ma on znaczenia.