如何在IEnumerable <object>上获取泛型参数的类型?

时间:2017-04-12 22:29:43

标签: c# generics reflection

我创建了一个方法来告诉我通用IEnumerable中对象的类型是什么。 这似乎是一个简单的事情,但当我尝试将值集合从Dictionary传递给我的方法时,我得到了意想不到的结果。 我想知道如何修复方法以返回正确的结果,理想情况下我还想解释为什么我得到了我得到的结果。

//sample class (target type to get)
public class Person
{
    public string Name { get; set; }    
    public int Age { get; set; }
}

//method that extracts the type
public Type GetItemType(IEnumerable<object> collection)
{
    Type collectionType = collection.GetType();
    Type[] genericArguments = collectionType.GetGenericArguments();
    return genericArguments[0];
}

//sample data for test
var folk = new Dictionary<string, Person>();
folk.Add("Joe", new Person() { Name="Joseph Stalin", Age = 43 });
folk.Add("Tony", new Person() { Name="Winston Churchill", Age = 65 });

IEnumerable<Person> people = folk.Values;
Type itemType = GetItemType(people);

itemType是&#34; System.String&#34;而不是&#34;人&#34;。 它似乎从实际的Dictionary中获取类型泛型参数,而不是值集合。

3 个答案:

答案 0 :(得分:1)

您遇到的问题是IEnumerable<object>背后有一个基础类型,而您的案例中的类型是Dictionary<string, Person>.ValueCollection

如果您使用调试器并检查collectionType,则可以看到。要解决此问题,您可以在初始化.ToList()时添加people,将集合转换为列表:

IEnumerable<Person> people = folk.Values.ToList();

现在IEnumerable<object>背后的类型为List<Person>,应该会为您提供所需的结果。

另一种“修复”方法是将您的方法签名更改为:

public Type GetItemType<T>(IEnumerable<T> collection)

即使没有将Values集合转换为列表,也会返回Person类型。

答案 1 :(得分:1)

这里的问题实际上是一个微妙的问题。发生了什么folk.Values的实际运行时类型是Dictionary的嵌套类。具体来说,它是Dictionary<string, Person>.ValueCollection。实际上,通用参数会移至ValueCollection类型,第一个参数最终为string

理想情况下,您真正​​需要做的就是更改方法签名:

public Type GetItemType<T>( IEnumerable<T> collection )
{
    return typeof( T );
}

要在不引入实际通用参数的情况下解决这个问题,您需要执行以下操作:

public Type GetItemType(IEnumerable<object> collection)
{
    Type collectionType = collection.GetType();
    collectionType.Dump();

    return collectionType.GetInterfaces()
        .Where( iface => iface.IsGenericType )
        .Where( iface => iface.GetGenericTypeDefinition() == typeof( IEnumerable<> ) )
        .Select( iface => iface.GetGenericArguments()[0] ).FirstOrDefault();
}

在这种情况下,我们枚举集合类型实现的接口寻找IEnumerable<>,然后拉出它的泛型参数。请注意,如果您发现使用不同泛型参数类型多次实现IEnumerable<>的类型,则会遇到问题。

答案 2 :(得分:0)

您的GetItemType方法获得了IEnumerable<object>。是否保证IEnumerable中的所有项目都是相同的类型?

如果没有,则需要返回IEnumerable类型。 如果是,您只需要查看第一个,然后返回collection.First().GetType()

这是你在找什么?

哦,实际上,请查看@ itsme86评论,这是一种更干净的方式来做你想做的事情