є спосіб для отримання типу T
з IEnumerable<T>
допомогою відображення?
напр
у мене є змінна IEnumerable<Child>
інформація; Я хочу знайти тип дитини через рефлексію
є спосіб для отримання типу T
з IEnumerable<T>
допомогою відображення?
напр
у мене є змінна IEnumerable<Child>
інформація; Я хочу знайти тип дитини через рефлексію
Відповіді:
IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0];
Таким чином,
IEnumerable<string> strings = new List<string>();
Console.WriteLine(strings.GetType().GetGenericArguments()[0]);
відбитки System.String
.
Див. MSDN для Type.GetGenericArguments
.
Редагувати: Я вважаю, що це вирішить проблеми в коментарях:
// 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]);
}
Деякі об'єкти реалізують більше одного загального, IEnumerable
тому необхідно повернути їх перерахування.
Редагувати: Хоча, я мушу сказати, це жахлива ідея для класу, яку можна реалізувати IEnumerable<T>
для більш ніж одного T
.
Я просто зробив метод розширення. Це працювало з усім, що я на це кинув.
public static Type GetItemType<T>(this IEnumerable<T> enumerable)
{
return typeof(T);
}
У мене була схожа проблема. Вибрана відповідь працює для фактичних примірників. У моєму випадку я мав лише тип (від a PropertyInfo
).
Вибрана відповідь не відповідає, коли сам тип typeof(IEnumerable<T>)
не є реалізацією IEnumerable<T>
.
У цьому випадку працює наступне:
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
- лише для версії dotNet FrameWork> = 4.5. В іншому випадку - використовувати Type.GetGenericArguments
замість цього.
Якщо ви знаєте IEnumerable<T>
(за допомогою дженериків), тоді просто typeof(T)
варто працювати. В іншому випадку (для object
або загального IEnumerable
) перевірте реалізовані інтерфейси:
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
параметром , а не як object obj
параметр , ви не можете просто замінити obj.GetType()
з , type
тому що якщо ви передаєте typeof(IEnumerable<T>)
ви нічого не отримаєте. Щоб обійти це, протестуйте type
себе, щоб переконатися, що це загальний IEnumerable<>
інтерфейс, а потім його інтерфейси.
Дуже дякую за дискусію. Я використовував це як основу для рішення нижче, яке добре працює у всіх цікавих для мене випадках (IEnumerable, похідні класи тощо). Я думав, що я повинен поділитися тут, якщо комусь це також потрібно:
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()
Просто використовуйте typeof(T)
EDIT: Або використовуйте .GetType (). GetGenericParameter () на об'єкті, який інстанціюється, якщо у вас немає T.
Альтернатива для більш простих ситуацій, коли це буде IEnumerable<T>
або замість, T
замітка .GenericTypeArguments
GetGenericArguments()
Type inputType = o.GetType();
Type genericType;
if ((inputType.Name.StartsWith("IEnumerable"))
&& ((genericType = inputType.GenericTypeArguments.FirstOrDefault()) != null)) {
return genericType;
} else {
return inputType;
}
Це є вдосконаленням рішення Елі Алгранті, оскільки воно також буде працювати там, де IEnumerable<>
тип знаходиться на будь-якому рівні в дереві спадкування.
Це рішення отримає тип елемента від будь-якого Type
. Якщо тип не є IEnumerable<>
, він поверне переданий тип. Для об'єктів використовуйте GetType
. Для типів використовуйте typeof
, а потім викликайте цей результат розширення за результатом.
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;
}
}
Я знаю, що це трохи старий, але я вважаю, що цей метод охопить усі проблеми та проблеми, викладені в коментарях. Подяка Елі Алгранті за натхнення на мою роботу.
/// <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();
}
Це рекурсивна функція, яка піде на глибину спочатку вниз по списку родових типів, поки не отримає конкретного визначення типу без внутрішніх родових типів.
Я перевірив цей метод із таким типом:
ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<IActionResult>>>>>>>>
які повинні повернутися IActionResult
typeof(IEnumerable<Foo>)
. поверне перший загальний аргумент - у цьому випадку .GetGenericArguments()
[0]
typeof(Foo)
Ось моя нечитабельна версія виразу запиту 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);
}
Зауважимо, що метод також враховує незагальний характер IEnumerable
, він повертається object
в цьому випадку, оскільки він бере Type
аргумент, а не конкретний екземпляр. До речі, для x являє собою невідоме , я знайшов це відео цікавим, хоча воно не має значення.