Получение типа T из IEnumerable <T>

Есть ли способ извлечь тип T из IEnumerable<T> через отражение?

например.

i имеет переменную IEnumerable<Child> info; я хочу, чтобы получить тип ребенка через отражение

Ответ 1

IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0]; 

Thusly,

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

Ответ 2

Я бы просто сделал метод расширения. Это работало со всем, что я бросил на него.

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

Ответ 3

У меня была похожая проблема. Выбранный ответ работает для реальных случаев. В моем случае у меня был только тип (из 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;
}

Ответ 4

Если вы знаете IEnumerable<T> (через generics), тогда просто 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);

Ответ 5

Большое спасибо за обсуждение. Я использовал его в качестве основы для решения ниже, которое хорошо работает для всех интересующих меня случаев (IEnumerable, производные классы и т.д.). Думаю, я должен поделиться здесь, если кто-то нуждается в этом:

  Type GetItemType(object someCollection)
  {
    var type = someCollection.GetType();
    var ienum = type.GetInterface(typeof(IEnumerable<>).Name);
    return ienum != null
      ? ienum.GetGenericArguments()[0]
      : null;
  }

Ответ 6

Просто используйте typeof(T)

EDIT: Или используйте .GetType(). GetGenericParameter() для объекта-объекта, если у вас нет T.

Ответ 7

Альтернатива для более простых ситуаций, когда она либо будет 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;
}

Ответ 8

Это улучшает решение Eli Algranti, поскольку оно также будет работать там, где тип 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;
    }
}

Ответ 9

Я знаю, что это немного устарело, но я верю, что этот метод будет охватывать все проблемы и проблемы, указанные в комментариях. Благодарим Эли Альгранти за вдохновение в мою работу.

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

Ответ 10

typeof(IEnumerable<Foo>). GetGenericArguments() [0] вернет первый общий аргумент - в этом случае typeof(Foo).

Ответ 11

Вот как я обычно это делаю (с помощью метода расширения):

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

Ответ 12

Вот моя нечитаемая версия выражения запроса 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 представляет неизвестное, я нашел это видео интересным, хотя это не имеет отношения..

Ответ 13

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