отримання типу T від IEnumerable <T>


106

є спосіб для отримання типу Tз IEnumerable<T>допомогою відображення?

напр

у мене є змінна IEnumerable<Child>інформація; Я хочу знайти тип дитини через рефлексію


1
У якому контексті? Що це за IE численні <T>? Це як примірник об'єкта, надісланий як аргумент? Або що?
Мехрдад Афшарі

Відповіді:


142
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.


Або ще гірше написати метод з віддачею і спробувати викликати GetType на змінній, створеній за допомогою цього методу. Це скаже вам, що це не подія загального типу. Так що в основному немає універсального способу отримати T з примірною змінною типу IEnumerable <T>
Дарин Димитров,

1
Або спробуйте з класом MyClass: IEnumerable <int> {}. У цього класу немає загального інтерфейсу.
Стефан Штейнеггер

1
Чому хтось коли-небудь вдасться отримати загальні аргументи, а потім захопити тип із свого індексатора? Це просто прохання про катастрофи, особливо коли мова підтримує типof (T), як @amsprich пропонує у своїй / її відповіді, який також може бути використаний як із загальним, так і з відомим типом ...
Роберт Петц

Це збивається з сумнівом при використанні із запитами linq - перший загальний аргумент WhereSelectEnumerableIterator - ні . Ви отримуєте загальний аргумент базового об'єкта, а не сам інтерфейс.
Pxtl

myEnumerable.GetType (). GetGenericArguments () [0] надає вам властивість FullName, яка вказує вам ім'я names.classname. Якщо ви шукаєте лише ім'я класу, використовуйте myEnumerable.GetType (). GetGenericArguments () [0]. Ім'я
user5534263

38

Я просто зробив метод розширення. Це працювало з усім, що я на це кинув.

public static Type GetItemType<T>(this IEnumerable<T> enumerable)
{
    return typeof(T);
}

6
Він не працюватиме, якщо посилання часу на компіляцію є лише типом об'єкта.
Штійн Ван Антверпен

27

У мене була схожа проблема. Вибрана відповідь працює для фактичних примірників. У моєму випадку я мав лише тип (від 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;
}

Врятував мені день. Для мого випадку я додав окремий оператор if для обробки рядків, оскільки він реалізує IEnumerable <char>
Edmund P Charumbira

Type.GenericTypeArguments- лише для версії dotNet FrameWork> = 4.5. В іншому випадку - використовувати Type.GetGenericArgumentsзамість цього.
Кое Кто

20

Якщо ви знаєте 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);

3
Деякі об'єкти реалізують більше одного загального IEnumerable.
Джейсон

5
@Jason - і в тих випадках питання "знайти Т" - це вже сумнівне питання; Я нічого не можу з цим зробити ...
Марк Гравелл

Один маленький Гоча для тих , хто намагається використовувати це з Type typeпараметром , а не як object objпараметр , ви не можете просто замінити obj.GetType()з , typeтому що якщо ви передаєте typeof(IEnumerable<T>)ви нічого не отримаєте. Щоб обійти це, протестуйте typeсебе, щоб переконатися, що це загальний IEnumerable<>інтерфейс, а потім його інтерфейси.
Ян Мерсер

8

Дуже дякую за дискусію. Я використовував це як основу для рішення нижче, яке добре працює у всіх цікавих для мене випадках (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()
Mass Dot Net

2

Просто використовуйте typeof(T)

EDIT: Або використовуйте .GetType (). GetGenericParameter () на об'єкті, який інстанціюється, якщо у вас немає T.


Ви не завжди маєте T.
Jason

Щоправда, у цьому випадку ви можете використовувати .GetType (). Я модифікую свою відповідь.
Відновлюйся

2

Альтернатива для більш простих ситуацій, коли це буде IEnumerable<T>або замість, Tзамітка .GenericTypeArgumentsGetGenericArguments()

Type inputType = o.GetType();
Type genericType;
if ((inputType.Name.StartsWith("IEnumerable"))
    && ((genericType = inputType.GenericTypeArguments.FirstOrDefault()) != null)) {

    return genericType;
} else {
    return inputType;
}

1

Це є вдосконаленням рішення Елі Алгранті, оскільки воно також буде працювати там, де 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;
    }
}

1

Я знаю, що це трохи старий, але я вважаю, що цей метод охопить усі проблеми та проблеми, викладені в коментарях. Подяка Елі Алгранті за натхнення на мою роботу.

/// <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<>);
}

1
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



0

ось як це я зазвичай роблю (методом розширення):

public static Type GetIEnumerableUnderlyingType<T>(this T iEnumerable)
    {
        return typeof(T).GetTypeInfo().GetGenericArguments()[(typeof(T)).GetTypeInfo().GetGenericArguments().Length - 1];
    }

0

Ось моя нечитабельна версія виразу запиту 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 являє собою невідоме , я знайшов це відео цікавим, хоча воно не має значення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.