委托实例和方法指针有什么区别?
我认为委托实例可以与函数实例互换。
采取以下代码:
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#,您将永远不会看到差异(至少到目前为止我还没有看到)。但是,我最近有非托管代码回调到此托管代码,它们的处理方式有所不同。例如,在一种情况下,如果我直接使用该函数作为回调(即使我的对象实例保留在周围),我会收到错误“在垃圾收集委托上进行了回调”。使用“委托实例”可以解决该问题。
有人知道有什么区别吗?
I thought that a delegate instance was interchangeable with a function instance.
Take the following code:
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());
}
Both ways of calling it APPEAR to be equivalent, and if you're using only C#, you'll never see the difference (at least I have not up to this point). However, I was recently unmanaged code that was calling back into this managed code, they are treated differently. For example, in one scenario, I to get the error "A callback was made on a garbage collected delegate" if I use the function directly as a callback (even though my object instance is kept around). Using the "delegate instance" fixes the problem.
Is there someone out there that knows what the difference is?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
术语更正:更合适的术语是方法组,而不是方法指针。
就功能而言,这两个语句是等效的。也就是说,它们产生几乎相同的IL。不同之处在于委托值的存储位置。
在第一种情况下,您直接将方法组 Add 传递到 MethodThatTakesAdd 。这会导致创建临时委托值,然后将其传递给 MethodThatTakesAdd。该委托值在 MethodThatTakesAdd 返回时会受到垃圾回收,因为它不存储该值。
在第二种情况下,您将委托分配给外部实例上的字段。这通常会增加委托的生命周期,从而减少在 pinvoke 调用期间它被垃圾收集的机会。
Terminology Corretion: Instead of method pointer, the more appropriate term is method group.
In terms of functionality the two statements are equivalent. That is that they produce almost the same IL. The difference is where the delegate value is stored.
In the first case you pass the method group Add to MethodThatTakesAdd directly. This causes a temporary delegate value to be created and then passed to MethodThatTakesAdd. This delegate value is subject to garbage collection the moment the MethodThatTakesAdd returns since it does not store the value.
In the second case you assigned the delegate to a field on the outer instance. This will typically increase the lifetime of the delegate and hence reduce the chance it's garbage collected during your pinvoke call.
委托是可调用的类,并且具有与函数指针类似的行为。委托内部存储要调用的函数的地址(即函数指针),但也提供其他功能,例如多播和存储调用列表;您基本上可以使用一个委托实例调用具有相同签名的许多函数,如下所示。
关于您关于
MethodThatTakesAdd(Add)
和MethodThatTakesAdd(DelegateInstance)
等价性的注释,如果您查看 C# 编译器为
MethodThatTakesAdd(Add)
行生成的 MSIL,您会注意到编译器正在创建委托并包装Add()
方法为你。Delegates are classes that are callable, and have similar behavior to function pointers. The delegate internally stores the address of the function to call (i.e. the function pointer), but also provides other functionality such as multi-casting and storing an invocation list; you can essentially invoke many functions of the same signature with one delegate instance as follows.
Regarding your note about the equivalence of
MethodThatTakesAdd(Add)
andMethodThatTakesAdd(DelegateInstance)
,if you look at the MSIL that the C# compiler generates for the line
MethodThatTakesAdd(Add)
, you will notice that the compiler is creating a delegate and wrapping theAdd()
method for you.虽然委托在 C# 中提供与 C 或 C++ 中的函数指针同义的功能,但仍存在显着差异。其中关键是委托是一个类,而不是指针。
简而言之,将委托转换为指针不会为您提供对函数或方法的引用,因此它不能用于通过非托管代码的引用来调用方法。
While delegates provide synonymous functionality in C# as function pointers in C or C++, there are significant differences. Key among these is that a delegate is a class, not a pointer.
In short, casting a delegate to a pointer isn't going to give you a reference to a function or method, and as such it can't be used to call a method by reference from unmanaged code.