我已经在网上阅读了很多关于安全发布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);
}
}
这样做是否足够?
答案 0 :(得分:12)
要发布对目标COM对象的引用,只需调用Marshal.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”之间打印出来。如果你构建调试(例如,CSC / debug GCTest.cs),“Finalized”将在“bye”之后打印出来,而在x
之前将Collect()
设置为null会“固定”那个。