我有一个带有私有方法的结构,我想调用它。由于我计划在性能关键部分执行此操作,因此我想缓存一个委托来执行操作。问题是我似乎无法使用Delegate.CreateDelegate绑定到它的方法。有问题的结构不是我的创建,用于与第三方库的交互。 有问题的结构看起来像这样::
public struct A
{
private int SomeMethod()
{
//body go here
}
}
以下代码将失败并显示“绑定到目标方法的错误”。
Delegate.CreateDelegate(typeof(Func<A,int>),typeof(A).GetMethod("SomeMethod",BindingFlags.Instance | BindingFlags.NonPublic));
我知道我可以编写一个表达式树来执行操作,但是我无法使用我的正常goto来处理这些Delegate.CreateDelegate
方法。
如果A
是一个类,上面的代码就可以了。问题只是因为A
是结构而产生的。
MSDN文档对于CreateDelegate的这个重载是不正确的,因为它对非静态方法起作用。
答案 0 :(得分:8)
有趣的问题。从这个错误报告中,看起来这可能是将在未来版本的.NET中修复的错误: http://connect.microsoft.com/VisualStudio/feedback/details/574959/cannot-create-open-instance-delegate-for-value-types-methods-which-implement-an-interface#details
编辑:实际上,我认为这个错误报告是关于一个不同的问题,所以你看到的行为实际上可能不是一个错误。
从错误报告中,我发现如果您将委托的第一个参数指定为通过引用传递,则可以解决这个问题。以下是一个完整的工作示例:
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's answer here也讨论了这个问题。
答案 1 :(得分:1)
未绑定实例方法委托的第一个参数不能是值类型。这是因为当用作'this'参数时必须处理值类型。您不能简单地通过值传递它们(如果您将值类型作为静态方法的第一个参数传递时会发生),因为该方法正在对副本执行操作,并且副本的任何变异都不会对原始文件产生影响
更新:如另一个答案所述,用作'this'参数的值类型通过引用有效传递。
答案 2 :(得分:0)
您正在使用this overload of CreateDelegate:
创建指定类型的委托以表示指定的静态方法。
SomeMethod
不是静态方法。
使用overload that allows to specify a target object:
A target = new A();
Func<int> f = (Func<int>)Delegate.CreateDelegate(
typeof(Func<int>),
target,
typeof(A).GetMethod(
"SomeMethod",
BindingFlags.Instance | BindingFlags.NonPublic));
这意味着您需要为A
的每个实例创建一个委托。您不能为不同的实例重用相同的委托。
最佳解决方案可能是使用LINQ表达式树构建Lambda表达式:
var p = Expression.Parameter(typeof(A), "arg");
var lambda = Expression.Lambda<Func<A, int>>(
Expression.Call(
p,
typeof(A).GetMethod(
"SomeMethod",
BindingFlags.Instance | BindingFlags.NonPublic)),
p);
Func<A, int> f = lambda.Compile();