只能在Type.IsGenericParameter为true的Type上调用方法
我在一个例程上得到这个错误,该例程使用reflection来转储一些对象属性,类似下面的代码。
MemberInfo[] members = obj.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance) ; foreach (MemberInfo m in members) { PropertyInfo p = m as PropertyInfo; if (p != null) { object po = p.GetValue(obj, null); ... } }
实际错误是“调用目标已抛出exception”,内部exception为“只能在Type.IsGenericParameter为true的Type上调用Method”。
在调试器obj的这个阶段出现
{Name = "SqlConnection" FullName = "System.Data.SqlClient.SqlConnection"}
使用System.RuntimeType类型
方法m是{System.Reflection.MethodBase DeclaringMethod}
请注意,obj的类型为System.RuntimeType,成员包含188个项目,而简单的typeof(System.Data.SqlClient.SqlConnection).GetMembers(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)仅返回65。
我尝试在obj和p.PropertyType上检查isGenericParameter,但对于大多数属性(包括p.GetValue工作的那些属性),这似乎都是错误的。
那么究竟什么是“Type.IsGenericParameter为真的类型”,更重要的是如何在没有try / catch的情况下避免这个错误?
首先,您已经做出了错误的假设,也就是说,您已经假定members
已经返回了System.Data.SqlClient.SqlConnection
实例的成员,而该members
尚未返回。 返回的是System.Type
实例的成员。
从DeclaringType的MSDN文档:
在
IsGenericParameter
属性为false的类型上获取IsGenericParameter
属性会引发InvalidOperationException
。
所以…抛出InvalidOperationException
是可以理解的,因为很自然,你不是在这里处理一个开放的generics类型。 有关开放generics类型的解释,请参阅Marc Gravells答案 。
那究竟什么是“Type.IsGenericParameter为真的类型”
这意味着它是开放generics类型中的generics类型参数 – 即我们还没有选择T
; 例如:
// true bool isGenParam = typeof(List<>).GetGenericArguments()[0].IsGenericParameter; // false (T is System.Int32) bool isGenParam = typeof(List).GetGenericArguments()[0].IsGenericParameter;
所以; 你有一些开放的仿制药吗? 也许你可以举一个例子来说明你的obj
来自哪里?
所有的线索都在那里。 obj的Type
是Type
类本身(或者更确切地说是奇怪的RuntimeType派生类)。
在失败时,你的循环已经到达名为DeclaringMethod
的Type
类属性。 但是,此类Type
实例所描述的Type
是System.Data.SqlClient.SqlConnection
,它不是方法的generics类型。
因此,尝试调用get on DeclaringMethod会导致exception。
关键是你正在检查类Type
。 它有点循环但想到这个: –
SqlConnection s = new SqlConnection(); Type t = s.GetType() Type ouch = t.GetType()
什么是类ouch描述?
如果没有try / catch,我该如何避免这个错误?
你几乎肯定不能。 当你调用p.GetValue
,你正在调用该属性的getter,这可能会抛出任何exception。 例如,如果连接关闭,SqlConnection.ServerVersion将抛出exception,您必须处理它。
这些额外成员来自哪里?
您的obj
已包含表示SqlConnection
的RuntimeType
对象,而不是SqlConnection
的实例。 obj.GetMembers()
将返回SqlConnection
类的65个成员,但通过再次调用GetType()
,您将获得188个RuntimeType
成员。
什么是IsGenericParameter
?
您可以拥有一个RuntimeType
实例,它代表一个类或方法的generics参数( List
的T
和TOutput
。在这种情况下,表示TOutput
的对象上的List
代替一个类,而不是代表一个类。让你得到一个表示ConvertAll<>
方法的MethodInfo
对象。但是,当RuntimeType
表示一个类时,声明方法的想法没有意义。这就是为什么读取属性会导致你看到的exception。