从.NET安全释放COM对象引用

从.NET安全释放COM对象引用,.net,com-interop,rcw,.net,Com Interop,Rcw,我在网上读了很多关于安全发布RCW的文章,在我看来,没有人能就需要按照什么顺序做什么达成一致,所以我想征求你们的意见。例如,可以这样做: object target = null; try { // Instantiate and use the target object. // Assume we know what we are doing: the contents of this try block // do in fact represent the ent

我在网上读了很多关于安全发布RCW的文章,在我看来,没有人能就需要按照什么顺序做什么达成一致,所以我想征求你们的意见。例如,可以这样做:

object target = null;
try {
    // Instantiate and use the target object.
    // Assume we know what we are doing: the contents of this try block
    // do in fact represent the entire desired lifetime of the COM object,
    // and we are releasing all RCWs in reverse order of acquisition.
} finally {
    if(target != null) {
        Marshal.FinalReleaseComObject(target);
        target = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}
然而,有些人主张在Marshal.FinalReleaseComObject之前进行垃圾收集,有些在之后进行,有些根本不进行垃圾收集。是否真的有必要手动GC每个RCW,特别是在它已经从COM对象分离之后

在我看来,将RCW与COM对象分离并让RCW自然过期会更简单、更容易:

object target = null;
try {
    // Same content as above.
} finally {
    if(target != null) {
        Marshal.FinalReleaseComObject(target);
    }
}

这样做就足够了吗?

要释放对目标COM对象的引用,只调用
封送。FinalReleaseComObject
而不是强制收集就足够了。换句话说,您已经履行了在完成引用后立即发布它的责任。我不会涉及
FinalReleaseComObject
vs
ReleaseComObject
的问题

这就留下了一个更大的问题:为什么人们主张调用
GC.Collect()
WaitForPendingFinalizers()

因为对于某些设计,很难知道何时不再有托管引用,因此无法安全地调用
ReleaseComObject
。你有两种选择,让记忆积累起来,希望收集发生,或者强制收集。[见评论中Steven Jansen的反对票]

另外需要注意的是,将
target
设置为
null
通常是不必要的,尤其是在示例代码中。将对象设置为零是VB6的常见做法,因为它使用基于引用计数的垃圾收集器。C#编译器足够聪明(在构建发布版时),知道
target
在最后一次使用后是不可访问的,甚至在离开作用域之前也可能被GC调用。最后一次使用是指最后一次可能的使用,因此在某些情况下,您可以将其设置为
null
。您可以通过下面的代码自己看到这一点:

   using System;
   class GCTest
   {
       ~GCTest() { Console.WriteLine("Finalized"); } 
       static void Main()
       {
           Console.WriteLine("hello");
           GCTest x = new GCTest();
           GC.Collect();
           GC.WaitForPendingFinalizers();
           Console.WriteLine("bye");
       }
   }

如果您构建版本(例如,CSC GCTest.cs),“Finalized”将在“hello”和“bye”之间打印出来。如果您构建debug(例如,CSC/debug GCTest.cs),“Finalized”将在“bye”之后打印,而在
Collect()
之前将
x
设置为null将“修复”这一问题。

否决票:这些人都建议相反,支持
GC.Collect
:,,,@Steve:谢谢链接。我已经更新了答案,以包含对您的注意事项的引用。@SteveJansen-前两个链接不建议调用
GC.Collect
。第一条还说“我们真的需要添加一个ReleaseComObjectFully()服务”——该服务后来被添加为
Marshal.FinalReleaseComObject
,这是Tony Lee在上面推荐的。MS链接关注当COM对象被重写为非COM对象时会发生什么,或者当它对多个客户端可见时会发生什么,因此必须小心地管理清理。这些担忧通常并不适用。