如何检测对象是否为通用集合,以及它包含哪些类型?

我有一个字符串序列化实用程序,它接受(几乎)任何类型的变量并将其转换为字符串。 因此,例如,根据我的惯例,整数值123将被序列化为“i:3:123”(i =整数; 3 =字符串的长度; 123 =值)。

该实用程序处理所有原始类型,以及一些非generics集合,如ArrayLists和Hashtables。 界面是这种forms

public static string StringSerialize(object o) {}

在内部我检测对象是什么类型并相应地序列化它。

现在我想升级我的实用程序来处理generics集合。 有趣的是,我找不到一个合适的函数来检测对象是一个generics集合,它包含哪些类型 – 我需要哪些信息才能正确序列化。 到目前为止,我一直在使用表格的编码

if (o is int) {// do something}

但这似乎不适用于generics。

您有什么推荐的吗?


编辑:感谢Lucero ,我已经接近答案了,但我仍然坚持这个小小的语法难题:

 if (t.IsGenericType) { if (typeof(List) == t.GetGenericTypeDefinition()) { Type lt = t.GetGenericArguments()[0]; List x = (List)o; stringifyList(x); } } 

此代码无法编译,因为“ lt ”不允许作为List对象的参数。 为什么不? 什么是正确的语法?

重新解决你的难题; 我假设stringifyList是一个通用的方法? 您需要使用reflection调用它:

 MethodInfo method = typeof(SomeType).GetMethod("stringifyList") .MakeGenericMethod(lt).Invoke({target}, new object[] {o}); 

其中{target}对于静态方法为null ,或者对于当前实例上的实例方法为null

进一步 – 我不会假设所有集合都是:基于List ,b:generics类型。 重要的是: 他们是否为某些T实现了IList

这是一个完整的例子:

 using System; using System.Collections.Generic; static class Program { static Type GetListType(Type type) { foreach (Type intType in type.GetInterfaces()) { if (intType.IsGenericType && intType.GetGenericTypeDefinition() == typeof(IList<>)) { return intType.GetGenericArguments()[0]; } } return null; } static void Main() { object o = new List { 1, 2, 3, 4, 5 }; Type t = o.GetType(); Type lt = GetListType(t); if (lt != null) { typeof(Program).GetMethod("StringifyList") .MakeGenericMethod(lt).Invoke(null, new object[] { o }); } } public static void StringifyList(IList list) { Console.WriteLine("Working with " + typeof(T).Name); } } 

使用“类型”收集所需信息。

对于通用对象,调用GetType()以获取其类型,然后检查IsGenericType以查明它是否是通用的。 如果是,您可以获得generics类型定义,例如,可以进行比较: typeof(List<>)==yourType.GetGenericTypeDefinition() 。 要找出generics类型是什么,请使用方法GetGenericArguments ,它将返回所使用类型的数组。

要比较类型,可以执行以下操作: if (typeof(int).IsAssignableFrom(yourGenericTypeArgument))


编辑回答后续行动:

只是让你的stringifyList方法接受一个IEnumerable (非generics)作为参数,也可能是已知的generics类型参数,你会没事的; 然后,您可以使用foreach所有项目并根据类型参数处理它们(如有必要)。

在最基本的层面上,所有通用列表都实现了IEnumerable ,它本身就是IEnumerable的后代。 如果要序列化列表,则可以将其强制转换为IEnumerable并枚举其中的generics对象。

你不能做的原因

 Type lt = t.GetGenericArguments()[0]; List x = (List)o; stringifyList(x); 

是因为generics仍然需要静态强类型,你要做的是创建一个动态类型。 ListList ,尽管使用相同的通用接口,但是两个完全不同的类型,并且您不能在它们之间进行转换。

 List intList = new List(); List strList = intList; // error! 

stringifyList(x)会接收什么类型? 你可以在这里传递的最基本的接口是IEnumerable ,因为IList不会从IListinheritance。

要序列化通用列表,您需要保留有关列表原始类型的信息,以便可以使用Activator重新创建。 如果要稍微优化以便不必在stringify方法中检查每个列表成员的类型,则可以直接传递从列表中提取的类型。