CreateDelegate() System.ArgumentException 方法签名不匹配

问题描述

我一直在尝试使用反射来比较编译时未知类型的对象,而不是每次尝试使用 CreateDelegate() 时都调用 Invoke()。到目前为止,我已经在基本类型等的通用类型类中使用它,但是我遇到了带有 keyvaluePair 类型的对象的砖墙。它抛出

System.ArgumentException: '无法绑定到目标方法,因为 其签名或安全透明度与 委托类型。'

调用 CreateDelegate() 时,即使方法签名与提供给 Invoke() 和从 Invoke() 返回的类型相匹配,但我无法弄清楚我做错了什么。重现异常的最少代码如下:

static void Main(string[] args)
{
    var kvp = new keyvaluePair<string,string>("test key","test value");

    var getKeyMethod = typeof(keyvaluePair<string,string>).GetProperty("Key").Getgetmethod();
    Console.WriteLine(getKeyMethod.Invoke(kvp,null));  //works fine

    var getKey = (Func<keyvaluePair<string,string>,string>) getKeyMethod.CreateDelegate(typeof(Func<keyvaluePair<string,string>));  //exception thrown here
    Console.WriteLine(getKey(kvp));  //never gets here
}

我意识到使用表达式树可以实现同样的事情,并且我已经使用完全相同的方法签名实现了这一点,如下所示:

    ParameterExpression targetExp = Expression.Parameter(typeof(keyvaluePair<string,string>),"target");
    MemberExpression propertyExp = Expression.Property(targetExp,typeof(keyvaluePair<string,string>).GetProperty("Key"));
    var getKeyMethod = Expression.Lambda<Func<keyvaluePair<string,string>>(propertyExp,targetExp).Compile();

同样,我想了解这里出了什么问题(表达式树也有点慢,但我主要只是因为无法使其正常工作而感到恼火)。

解决方法

KeyValuePair<,> 是一个结构体。结构体和委托有特殊的规则。要访问实例方法,您本质上需要一个带有 ref 参数的委托。例如,以下内容按预期工作:

// define delegate
delegate string RefDel(ref KeyValuePair<string,string> kvp);

// in method 
var kvp = new KeyValuePair<string,string>("test key","test value");

var getKeyMethod = typeof(KeyValuePair<string,string>).GetProperty("Key").GetGetMethod();
var getKey = (RefDel)getKeyMethod.CreateDelegate(typeof(RefDel));

Console.WriteLine(getKey(ref kvp));  // "test key"

See here 为一个活生生的例子。