如何从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();