只能在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的TypeType类本身(或者更确切地说是奇怪的RuntimeType派生类)。

在失败时,你的循环已经到达名为DeclaringMethodType类属性。 但是,此类Type实例所描述的TypeSystem.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已包含表示SqlConnectionRuntimeType对象,而不是SqlConnection的实例。 obj.GetMembers()将返回SqlConnection类的65个成员,但通过再次调用GetType() ,您将获得188个RuntimeType成员。

什么是IsGenericParameter

您可以拥有一个RuntimeType实例,它代表一个类或方法的generics参数( List.ConvertAllTTOutput 。在这种情况下,表示TOutput的对象上的List.ConvertAll代替一个类,而不是代表一个类。让你得到一个表示ConvertAll<>方法的MethodInfo对象。但是,当RuntimeType表示一个类时,声明方法的想法没有意义。这就是为什么读取属性会导致你看到的exception。