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

时间:2010-12-01 16:47:48

标签: c# delegates

我有一个带有私有方法的结构,我想调用它。由于我计划在性能关键部分执行此操作,因此我想缓存一个委托来执行操作。问题是我似乎无法使用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的这个重载是不正确的,因为它对非静态方法起作用。

3 个答案:

答案 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();