C# 如何获取IEnumerable<;上的泛型参数类型;对象>;?

C# 如何获取IEnumerable<;上的泛型参数类型;对象>;?,c#,generics,reflection,C#,Generics,Reflection,我创建了一个方法来告诉我泛型IEnumerable中对象的类型。 这看起来很简单,但当我尝试将值集合从字典传递到我的方法时,我得到了意想不到的结果。 我想知道如何修复该方法以返回正确的结果,理想情况下,我还想解释一下为什么会得到我得到的结果 //sample class (target type to get) public class Person { public string Name { get; set; } public int Age { get; set;

我创建了一个方法来告诉我泛型IEnumerable中对象的类型。 这看起来很简单,但当我尝试将值集合从字典传递到我的方法时,我得到了意想不到的结果。 我想知道如何修复该方法以返回正确的结果,理想情况下,我还想解释一下为什么会得到我得到的结果

//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);
//示例类(要获取的目标类型)
公共阶层人士
{
公共字符串名称{get;set;}
公共整数{get;set;}
}
//方法来提取类型
公共类型GetItemType(IEnumerable集合)
{
Type collectionType=collection.GetType();
类型[]genericArguments=collectionType.GetGenericArguments();
返回泛型参数[0];
}
//测试样本数据
var folk=新字典();
folk.Add(“Joe”,newperson(){Name=“约瑟夫·斯大林”,年龄=43});
folk.Add(“托尼”,新人(){Name=“温斯顿·丘吉尔”,年龄=65});
IEnumerable people=民间价值观;
Type itemType=GetItemType(人);
itemType是“System.String”,而不是“Person”。
它似乎从实际字典中获取类型泛型参数,而不是值集合。

您的
GetItemType
方法获得一个
IEnumerable
。是否保证IEnumerable中的所有项都是相同类型的

如果不是,则需要返回类型的IEnumerable。 如果是,您只需查看第一个,并返回
collection.first().GetType()

这就是你要找的吗


哦,事实上,请查看@itsme86 comment,这是一种更简洁的方式来做你想做的事情

你面临的问题是,
IEnumerable
背后有一个底层类型,而你的案例中的类型是
Dictionary.ValueCollection

如果使用调试器并检查
collectionType
,可以看到这一点。要解决这个问题,您可以在初始化
人员时通过添加
.ToList()
将集合转换为列表:

IEnumerable<Person> people = folk.Values.ToList();
IEnumerable people=folk.Values.ToList();
现在
IEnumerable
后面的类型是
List
,应该会给出您要查找的结果

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

public Type GetItemType<T>(IEnumerable<T> collection)
public类型GetItemType(IEnumerable集合)

这将返回一个
Person
类型,即使没有将值集合转换为列表。

这里的问题实际上是一个微妙的问题。实际上,
folk.Values
的实际运行时类型是
Dictionary
的嵌套类。具体地说,它是Dictionary.ValueCollection
。实际上,泛型参数被移动到
ValueCollection
类型上,而第一个参数最终是
string

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

public Type GetItemType<T>( IEnumerable<T> collection )
{
    return typeof( T );
}
public类型GetItemType(IEnumerable集合)
{
返回类型(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();
}
public类型GetItemType(IEnumerable集合)
{
Type collectionType=collection.GetType();
collectionType.Dump();
返回collectionType.GetInterfaces()
.Where(iface=>iface.IsGenericType)
.Where(iface=>iface.GetGenericTypeDefinition()==typeof(IEnumerable))
.Select(iface=>iface.GetGenericArguments()[0]).FirstOrDefault();
}

在本例中,我们枚举集合类型实现的接口,查找
IEnumerable
,然后提取其泛型参数。注意,如果您发现一个类型多次使用不同的泛型参数类型实现了
IEnumerable
,那么这可能会遇到问题。

很好奇为什么选择该参数类型。这个方法似乎很简单:
public-Type-GetItemType(IEnumerable-collection){return-typeof(T);}
I实际上没有泛型参数T。@SynBiotik-then-make;)这就是GetItemType中的功能。它也不必在类级别上定义,我编写该示例是为了简单。此用例用于序列化帮助器。我目前传递了泛型参数T,但是导致这个的代码要详细得多。这就是我想要改变的。但不仅仅是修复。我想了解这一点。可以保证IEnumerable中的所有项都是相同类型的,但是获取第一个元素对我没有帮助,因为集合是空的。就个人而言,我会避免建议调用
ToList()
。特别是如果集合的大小很大,复制整个集合只是为了将类型转换为可用的类型,这对我来说开销太大了。但这肯定是一种方法可能风险稍低的
ToList()
方法是先调用
Take(0)
。例如,
folk.Values.Take(0.ToList()
。然后您将得到
列表
对象,其中没有任何内容。这样做将允许OP的原始方法代码(比公认答案中较长的反射稍微简单)按原样工作。这将起作用,但我强烈鼓励使用推荐的更简单、更有效的方法,将方法更改为泛型并使用类型参数。至少对于问题中所示的场景,类型在编译时可用,这是一种更好的方法。当然,如果出于某种原因,该方法无法通用或必须处理各种接口(例如,当实际类型为
IEnumerable
)时,您所能得到的只是
IEnumerable
)@PeterDuniho确实是理想的,但这似乎不符合OP的要求(无论出于何种原因)。我会把它作为一个附加的考试