Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 类中的每个回调都需要gchandle.alloc()吗?_C#_.net_Delegates_Callback - Fatal编程技术网

C# 类中的每个回调都需要gchandle.alloc()吗?

C# 类中的每个回调都需要gchandle.alloc()吗?,c#,.net,delegates,callback,C#,.net,Delegates,Callback,我有一个.NT类,它有多个用于从本机代码回调的委托。是否需要分配所有代表?我的意思是GCHandle.Alloc()是否只保护委托或拥有委托的整个类不被收集?委托有两个相关属性,即方法和目标。如果委托是为实例方法创建的,则目标将为非null。只要垃圾收集器能够看到委托实例,就可以保持对象的活动状态 本机代码与回调问题相关。当您将委托实例传递给pinvoked本机函数时,P/Invoke封送器将使用Marshal.GetFunctionPointerForDelegate()创建一个小存根,在本机

我有一个.NT类,它有多个用于从本机代码回调的委托。是否需要分配所有代表?我的意思是
GCHandle.Alloc()
是否只保护委托或拥有委托的整个类不被收集?

委托有两个相关属性,即方法和目标。如果委托是为实例方法创建的,则目标将为非null。只要垃圾收集器能够看到委托实例,就可以保持对象的活动状态

本机代码与回调问题相关。当您将委托实例传递给pinvoked本机函数时,P/Invoke封送器将使用Marshal.GetFunctionPointerForDelegate()创建一个小存根,在本机代码进行回调时生成所需的目标引用。但是,垃圾收集器无法看到此存根,因此无法找到对委托对象的引用。并收集它。来自本机代码的下一次回调将产生崩溃


为了避免这种情况,您必须自己存储委托对象,以便只要本机代码能够进行回调,委托对象就会一直被引用。将其存储在静态变量中是一个显而易见的解决方案。

我认为当您仅存储委托对象时,会发生一些错误(但通常不会发生)。 众所周知,托管内存将通过垃圾收集进行安排。(这意味着将更改托管对象的物理内存地址。)

如果本机代码要调用一个长寿命委托,我们将该委托设置为静态成员或类成员。但是有时候(我们不知道什么时候,我们只知道它会发生),GC安排的内存,以及委托的物理内存可能从0x000000A到0x0000010。但是本机代码对此一无所知,对于本机代码,它只知道永远在0x000000A处调用。 因此,我们不仅应该存储委托对象,还应该使用GCHandle.Alloc告诉GC不要移动委托对象的物理内存。然后本机代码将在回调时运行良好

嗯,因为GC不经常安排托管内存,所以在很短的时间内,即使你不调用GCHandle.Alloc,你的代码总是“做得很好”,但有时它会疯掉。 现在,你知道原因了

参考: 我错了。 您不需要固定委托(另一方面,您不能固定委托,将引发异常(System.ArgumentException:对象包含非基本数据或非blittable数据)

参考:

在克里斯·布鲁姆的博客中详细介绍了这一点

克里斯·布鲁姆写道: 同样,托管委托可以封送到非托管代码,在非托管代码中它们作为非托管函数指针公开。对这些指针的调用将执行非托管到托管的转换;调用约定的更改;进入正确的AppDomain;以及任何必要的参数封送。显然是非托管函数指针必须引用固定地址。如果GC重新定位该地址,这将是一场灾难!这将导致许多应用程序为委托创建固定句柄。这完全没有必要。非托管函数指针实际上引用我们动态生成的本机代码存根,以执行转换和封送处理。此存根位于GC堆外部的固定内存中的sts

但是,应用程序负责以某种方式延长委托的生存期,直到不再发生来自非托管代码的调用。本机代码存根的生存期与委托的生存期直接相关。一旦收集委托,通过非托管函数指针的后续调用将崩溃或以其他方式停止中断这个过程。在我们最近的版本中,我们添加了一个客户调试探测,它允许您在代码中清晰地检测到这个非常常见的错误。如果您在开发过程中还没有开始使用客户调试探测,请看一看


使用GCHandle.AllocKinda late comment时,您不需要固定委托,但将其存储在
static
变量中是否也可以防止其他答案中提到的重新排列?固定对象是完全不同的,而不是此处的主题。回调委托不需要固定。其他答案(以及我检查过的其他一些来源)确实提到,如果回调委托被移动到其他地址,则本机层在尝试调用它时会崩溃,因为它所拥有的地址不再有效。或者我弄错了?