【问题标题】:How can I create an open Delegate from a struct's instance method?如何从结构的实例方法创建一个打开的委托?
【发布时间】:2011-05-18 14:59:21
【问题描述】:

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

【问题讨论】:

  • 出于好奇,为什么要使用结构实例方法?使用结构实例方法创建委托实际上需要对结构进行装箱才能使用它,因此装箱的结构实例将有效地表现为类类型。为什么不只使用一个类开始(即使该类只包含一个结构类型的字段)?
  • 这是一个老问题,但结构来自第三方库。但是,使用委托不会装箱或复制它,因为结构实例方法是“按引用”的。我需要在他们给我的句柄上调用一个方法。因为他们最后才叫它,但我想早点做(我不记得了)。
  • 我猜如果您打算将 struct 方法视为采用 ref 参数的静态方法(而不是从未绑定委托创建绑定委托),您可以避免拳击,虽然在那种情况下我认为使用静态结构方法会更清楚。
  • 再说一次,不是我的代码。我需要调用第三方的私有实现细节,因为他们有点过于保守了。它们的句柄充当一系列对象的代理。

标签: c# delegates


【解决方案1】:

有趣的问题。从这个错误报告来看,这可能是一个将在未来版本的 .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 也讨论了这个问题。

【讨论】:

  • 有趣;我没有意识到将值类型指定为“ref”会允许它绑定。
【解决方案2】:

未绑定实例方法委托的第一个参数不能是值类型。这是因为在用作“this”参数时必须如何处理值类型。您不能简单地按值传递它们(如果您将值类型作为静态方法的第一个参数传递,则会发生这种情况),因为该方法正在作用于副本,并且副本的任何突变都不会影响原始.


更新:如另一个答案中所述,用作“this”参数的值类型通过引用有效地传递。

【讨论】:

  • 感谢您澄清为什么将 this 参数指定为通过引用传递对于值类型是必要的。你的解释很有道理。
【解决方案3】:

你正在使用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();

【讨论】:

  • 嗯。不,我想使用“开放”代表。意思是没有目标。如果 A 是一个类,上面的代码将是完全有效的。似乎 MSDN 文档也很糟糕:它还说这个 注意 当委托未在其第一个参数上关闭时,应使用此方法重载,因为在这种情况下它会更快一些。使用开放委托的整个想法不是为每个潜在目标创建一个委托。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-05
  • 2015-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-05
相关资源
最近更新 更多