如何从struct的实例方法创建一个开放的Delegate?

我有一个带有私有方法的结构,我想调用它。 由于我计划在性能关键部分执行此操作,因此我想缓存一个委托以执行操作。 问题是我似乎无法使用Delegate.CreateDelegate绑定到它的方法。 有问题的结构不是我的创建,用于与第三方库的交互。 有问题的结构看起来像这样::

public struct A { private int SomeMethod() { //body go here } } 

以下代码将失败,并出现“绑定到目标方法的错误”。

 Delegate.CreateDelegate(typeof(Func),typeof(A).GetMethod("SomeMethod",BindingFlags.Instance | BindingFlags.NonPublic)); 

我知道我可以编写一个表达式树来执行该操作,但似乎奇怪的是我无法使用我的普通goto来处理Delegate.CreateDelegate方法。

如果A是一个类,上面的代码就可以了。 这个问题只是因为A是结构而产生的。 MSDN文档对于CreateDelegate的这个重载是不正确的,因为它对非静态方法起作用。

有趣的问题。 从这个错误报告中,看起来这可能是将在未来版本的.NET中修复的错误: http : //connect.microsoft.com/VisualStudio/feedback/details/574959/cannot-create-open-instance -delegate换值类型的方法,其中实施的安接口#细节

编辑:实际上,我认为这个错误报告是关于一个不同的问题,所以你看到的行为实际上可能不是一个错误。

从那个bug报告中,我发现如果你指定你的委托的第一个参数是通过引用传递的,那么有一个解决方法。 以下是一个完整的工作示例:

 public struct A { private int _Value; public int Value { get { return _Value; } set { _Value = value; } } private int SomeMethod() { return _Value; } } delegate int SomeMethodHandler(ref A instance); class Program { static void Main(string[] args) { var method = typeof(A).GetMethod("SomeMethod", BindingFlags.Instance | BindingFlags.NonPublic); SomeMethodHandler d = (SomeMethodHandler)Delegate.CreateDelegate(typeof(SomeMethodHandler), method); A instance = new A(); instance.Value = 5; Console.WriteLine(d(ref instance)); } } 

编辑: Jon Skeet在这里的答案也讨论了这个问题。

未绑定实例方法委托的第一个参数不能是值类型。 这是因为当用作’this’参数时必须处理值类型。 您不能简单地通过值传递它们(如果您将值类型作为静态方法的第一个参数传递时会发生),因为该方法正在对副本执行操作,并且副本的任何变异都不会对原始文​​件产生影响。


更新:如另一个答案所述,当用作’this’参数时,值类型通过引用有效传递。

你正在使用CreateDelegate的这个重载 :

创建指定类型的委托以表示指定的静态方法。

SomeMethod不是静态方法。

使用允许指定目标对象的重载 :

 A target = new A(); Func f = (Func)Delegate.CreateDelegate( typeof(Func), target, typeof(A).GetMethod( "SomeMethod", BindingFlags.Instance | BindingFlags.NonPublic)); 

这意味着您需要为A每个实例创建一个委托。 您不能为不同的实例重用相同的委托。

最好的解决方案可能是使用LINQ表达式树构建Lambda表达式:

 var p = Expression.Parameter(typeof(A), "arg"); var lambda = Expression.Lambda>( Expression.Call( p, typeof(A).GetMethod( "SomeMethod", BindingFlags.Instance | BindingFlags.NonPublic)), p); Func f = lambda.Compile();