получение типа T из IEnumerable <T> - PullRequest
95 голосов
/ 25 мая 2009

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

, например

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

Ответы [ 12 ]

132 голосов
/ 25 мая 2009
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.

37 голосов
/ 14 марта 2012

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

public static Type GetItemType<T>(this IEnumerable<T> enumerable)
{
    return typeof(T);
}
23 голосов
/ 18 июля 2013

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

Если вы знаете 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);
7 голосов
/ 21 мая 2010

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

  Type GetItemType(object someCollection)
  {
    var type = someCollection.GetType();
    var ienum = type.GetInterface(typeof(IEnumerable<>).Name);
    return ienum != null
      ? ienum.GetGenericArguments()[0]
      : null;
  }
2 голосов
/ 24 апреля 2013

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

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

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

1 голос
/ 19 марта 2019

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

/// <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 голос
/ 14 июля 2016

Это улучшение решения 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;
    }
}
0 голосов
/ 16 июня 2019

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...