从 IEnumerable 获取类型 T;

发布于 2024-10-17 14:56:29 字数 161 浏览 4 评论 0 原文

有没有办法通过反射从 IEnumerable 检索类型 T

例如

我有一个变量 IEnumerable info;我想通过反射检索 Child 的类型

is there a way to retrieve type T from IEnumerable<T> through reflection?

e.g.

i have a variable IEnumerable<Child> info; i want to retrieve Child's type through reflection

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

柠北森屋 2024-10-24 14:56:29
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,因此有必要返回它们的枚举。

编辑:尽管如此,我不得不说,对于一个类来说,为多个 T 实现 IEnumerable 是一个糟糕的主意。

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

Thusly,

IEnumerable<string> strings = new List<string>();
Console.WriteLine(strings.GetType().GetGenericArguments()[0]);

prints System.String.

See MSDN for Type.GetGenericArguments.

Edit: I believe this will address the concerns in the comments:

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

Some objects implement more than one generic IEnumerable so it is necessary to return an enumeration of them.

Edit: Although, I have to say, it's a terrible idea for a class to implement IEnumerable<T> for more than one T.

心如狂蝶 2024-10-24 14:56:29

我只是做一个扩展方法。这与我投入的一切都有效。

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

I'd just make an extension method. This worked with everything I threw at it.

public static Type GetItemType<T>(this IEnumerable<T> enumerable)
{
    return typeof(T);
}
终陌 2024-10-24 14:56:29

我有类似的问题。所选答案适用于实际情况。
就我而言,我只有一个类型(来自 PropertyInfo)。

当类型本身是 typeof(IEnumerable) 而不是 IEnumerable 的实现时,所选答案将失败。

对于这种情况,可以进行以下操作:

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

I had a similar problem. The selected answer works for actual instances.
In my case I had only a type (from a PropertyInfo).

The selected answer fails when the type itself is typeof(IEnumerable<T>) not an implementation of IEnumerable<T>.

For this case the following works:

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;
}
如果没结果 2024-10-24 14:56:29

如果您知道 IEnumerable (通过泛型),那么只需 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);

If you know the IEnumerable<T> (via generics), then just typeof(T) should work. Otherwise (for object, or the non-generic IEnumerable), check the interfaces implemented:

        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);
无所谓啦 2024-10-24 14:56:29

非常感谢您的讨论。我用它作为下面解决方案的基础,它适用于我感兴趣的所有情况(IEnumerable、派生类等)。我想我应该在这里分享,以防有人也需要它:

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

Thank you very much for the discussion. I used it as a basis for the solution below, which works well for all cases that are of interest to me (IEnumerable, derived classes, etc). Thought I should share here in case anyone needs it also:

  Type GetItemType(object someCollection)
  {
    var type = someCollection.GetType();
    var ienum = type.GetInterface(typeof(IEnumerable<>).Name);
    return ienum != null
      ? ienum.GetGenericArguments()[0]
      : null;
  }
徒留西风 2024-10-24 14:56:29

我知道这有点老了,但我相信这种方法将涵盖评论中指出的所有问题和挑战。感谢 Eli Algranti 对我​​工作的启发。

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

I know this is a bit old, but I believe this method will cover all the problems and challenges stated in the comments. Credit to Eli Algranti for inspiring my work.

/// <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<>);
}
和影子一齐双人舞 2024-10-24 14:56:29
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>>>

应该返回 T

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

This is a recursive function that will go depth first down the list of generic types until it gets a concrete type definition with no inner generic types.

I tested this method with this type:
ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<T>>>>>>>>

which should return T

烟雨凡馨 2024-10-24 14:56:29

只需使用 typeof(T)

编辑:
或者,如果您没有 T,请在实例化对象上使用 .GetType().GetGenericParameter()。

Just use typeof(T)

EDIT:
Or use .GetType().GetGenericParameter() on an instantiated object if you don't have T.

傲影 2024-10-24 14:56:29

对于更简单的情况,它是 IEnumerableT 的替代方案 - 请注意使用 GenericTypeArguments 而不是 GetGenericArguments ()

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

    return genericType;
} else {
    return inputType;
}

An alternative for simpler situations where it's either going to be an IEnumerable<T> or T - note use of GenericTypeArguments instead of GetGenericArguments().

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

    return genericType;
} else {
    return inputType;
}
放血 2024-10-24 14:56:29

这是对 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;
    }
}

This is an improvement on Eli Algranti's solution in that it will also work where the IEnumerable<> type is at any level in the inheritance tree.

This solution will obtain the element type from any Type. If the type is not an IEnumerable<>, it will return the type passed in. For objects, use GetType. For types, use typeof, then call this extension method on the result.

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;
    }
}
冰葑 2024-10-24 14:56:29

typeof(IEnumerable)GetGenericArguments()[0] 将返回第一个泛型参数 - 在本例中为 typeof(Foo)

typeof(IEnumerable<Foo>).GetGenericArguments()[0] will return the first generic argument - in this case typeof(Foo).

薄情伤 2024-10-24 14:56:29

这就是我通常这样做的方式(通过扩展方法):

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

this is how I usually do it (via extension method):

public static Type GetIEnumerableUnderlyingType<T>(this T iEnumerable)
    {
        return typeof(T).GetTypeInfo().GetGenericArguments()[(typeof(T)).GetTypeInfo().GetGenericArguments().Length - 1];
    }
青春如此纠结 2024-10-24 14:56:29

这是我不可读的 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代表未知,我发现这个视频很有趣,尽管它无关紧要..

Here's my unreadable Linq query expression version ..

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

Note the method also takes non-generic IEnumerable into account, it returns object in this case, because it takes a Type rather than a concrete instance as the argument. By the way, for x represents the unknown, I found this video insteresting, though it is irrelevant ..

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文