如何检测对象是否为通用集合,以及它包含哪些类型?
我有一个字符串序列化实用程序,它接受(几乎)任何类型的变量并将其转换为字符串。 因此,例如,根据我的惯例,整数值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仍然需要静态强类型,你要做的是创建一个动态类型。 List
和List
,尽管使用相同的通用接口,但是两个完全不同的类型,并且您不能在它们之间进行转换。
List intList = new List (); List strList = intList; // error!
stringifyList(x)
会接收什么类型? 你可以在这里传递的最基本的接口是IEnumerable
,因为IList
不会从IList
inheritance。
要序列化通用列表,您需要保留有关列表原始类型的信息,以便可以使用Activator
重新创建。 如果要稍微优化以便不必在stringify方法中检查每个列表成员的类型,则可以直接传递从列表中提取的类型。