委托实例和方法指针有什么区别?

9

我以为委托实例可以和函数实例通用。

看下面的代码:

delegate int AddDelegate(int a, int b);

AddDelegate DelegateInstance;

public void DoStuff()
{
    //I can call this without a delegate "instance":
    MethodThatTakesAdd(Add);

    //I can also call it WITH a delegate "instance"
    DelegateInstance = Add;
    MethodThatTakesAdd(DelegateInstance);
}

public int Add(int a, int b)
{
    return a + b;
}

public void MethodThatTakesAdd(AddDelegate addFunction)
{
    Console.WriteLine(addFunction(1, 2).ToString());
}

两种调用方式看起来是等价的,如果您只使用C#,您不会看到任何区别(至少在我目前的使用中没有)。然而,最近我遇到了一些调用非托管代码的情况,它们对这个托管代码进行了回调,它们被不同地处理。例如,在某种情况下,如果我直接将函数用作回调(即使我的对象实例保持不变),我会收到“在垃圾回收委托上进行了回调”的错误。使用“委托实例”可以解决这个问题。
有人知道它们之间的区别吗?

1
il是什么样子的?有没有“在底层”的迹象表明它们通常会被不同地处理? - Michael Todd
3个回答

13

术语更正:“方法指针”不太准确,更合适的术语应该是“方法组”。

就功能而言,这两个语句是等价的。也就是说,它们产生的IL代码几乎相同。差异在于委托值存储的位置。

在第一种情况下,你直接将方法组Add传递给MethodThatTakesAdd。这将导致创建一个临时委托值,然后将其传递给MethodThatTakesAdd。由于没有存储该值,因此该委托值在MethodThatTakesAdd返回时立即成为垃圾回收的对象。

在第二种情况下,你将委托分配给外部实例的字段。这通常会增加委托的生命周期,因此减少了它在pinvoke调用期间被垃圾回收的机会。


那听起来非常可信,可以解释我所看到的! - Jason Young
http://msdn.microsoft.com/en-us/magazine/cc164193.aspx 这篇文章在“Reverse P/Invoke and Delegate Lifetime”一节中详细阐述了它。 - Ihar Bury
@JaredPar 我不明白。这个 AddDelegate DelegateInstance; 是一个委托 实例 吗?它只是一个类型为 AddDelegate 的变量。看起来委托 实例 应该是 myDel += new MYDEL(myMethod);右侧 -- 或者说 myDel += myMethod;(简写).....我错了吗? - Royi Namir

4
委托是可调用的类,其行为类似于函数指针。该委托内部存储要调用的函数的地址(即函数指针),但还提供其他功能,如多路广播和存储调用列表;您可以使用一个委托实例调用许多具有相同签名的函数。
public void DoStuff()
{
    DelegateInstance += Add;
    DelegateInstance += AnotherAdd;
    DelegateInstance += YetAnotherAdd;

    // Invoke Add(100, 200), AnotherAdd(100, 200), and YetAnotherAdd(100, 200)
    DelegateInstance(100, 200);
}

关于您提到的MethodThatTakesAdd(Add)MethodThatTakesAdd(DelegateInstance)等价的注释, 如果您查看C#编译器为MethodThatTakesAdd(Add)生成的MSIL代码,您会注意到编译器正在为您创建委托并包装Add()方法。


0

虽然在C#中,委托(Delegate)和C或C++中的函数指针提供了同义的功能,但二者存在一些重大差异。其中最关键的是委托是一个类,而不是一个指针。

简而言之,将委托强制转换为指针并不能让您获得对函数或方法的引用,因此无法从非托管代码中通过引用调用方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接