我正在使用Microsoft.Interropt.Excel DLL编写Excel类。 我完成了所有功能,但我的析构函数中有错误。 我想将所有更改保存到我的文件中,并且我想要释放所有源代码。我希望所有这些都在我的析构函数中。 但在我的析构函数中,Excel.ApplicationClass,Workbook和Worksheet对象由异常填充,其中包含消息“已与其基础RCW分离的COM对象无法使用”。 因此我无法保存任何内容,因为ı无法访问工作簿或工作表对象而无法保密。
我无法访问Destructor中的类私有成员吗?
答案 0 :(得分:16)
.NET最接近析构函数的是.NET称之为终结器的东西。主要区别在于析构函数通常具有确定性终结(例如,当对象上的引用计数变为零时),而在不再引用对象后,在未确定时间调用.NET终结器。这由.NET垃圾收集器使用根跟踪过程处理,而不是使用简单的引用计数。
最好的文章之一是Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework。有关终结器的更多信息,请参阅MSDN中的文章Finalize Methods and Destructors。
我无法访问私有类 Destructor中的成员?
不,你不能安全地这样做。
在您的情况下发生的情况是,当您的对象不再被根直接或间接引用时,您的对象引用的COM对象(即您的私有字段引用的对象)也不会被引用由根也是。 (由对象的字段引用 not 使这些COM对象保持活动状态,因为您的对象不再被根引用或从根跟踪,因此,COM对象不会从根跟踪或者。)所以你的对象和它引用的所有COM对象都准备好同时进行垃圾收集 。一段时间后,垃圾收集器将清理你的对象并调用它的终结器,因为它也会对COM对象进行清理,每个对象都是Runtime Callable Wrapper (RCW)。
麻烦的是,不仅是这些对象何时被垃圾收集不确定的时间,而且调用终结器的 order 也是不确定的。在这种情况下,运行时可调用包装器也有一个终结器,它自己调用Marshal.ReleaseComObject,其结果是减少围栅COM侧的引用计数,以便释放该COM对象。但由于调用终结器的顺序是不确定的,因此对象引用的COM对象的终结器很可能会在>强化对象的终结器之前触发。因此,终结器中的代码有时会工作,但是,大多数情况下,对象引用的一个或多个Runtime Callable Wrappers已经调用了终结器,而底层的COM对象将会已经发布,在你的终结者执行其代码之前。
简而言之,您应该避免使用终结器,并且永远不要在终结器中访问引用类型,因为这些引用类型可能已经完成。
为了纠正你的情况,我会考虑两种不同的可能性:
通过使用IDisposable interface启用对象的确定性处理,而不是依赖于非确定性终结器。
有关如何实现IDisposable模式的文章,请参阅:
- 迈克
答案 1 :(得分:1)
我不确定我是否编码错误 - 试着按照这里的例子。我发现当我利用IDisposable模式时,一切都有效,除非我需要处理工作簿事件。
在我的场景中,用户可以在关闭应用程序之前关闭工作簿。我已经声明了Excel对象WithEvents并编写了WorkbookBeforeClose处理程序以满足要求。
在这种情况下,当我关闭我的应用程序时(我已经关闭了Excel),我得到“与其底层RCW分离的COM对象无法使用”错误。调用Dispose(False)时,Finalize中会发生错误。
如果我保留使用事件声明但不编码任何处理程序的Excel对象,问题就会消失。
在我的Dispose中,我不得不吞下我的Workbook的错误。关闭并退出,因为它们是导致错误的语句。
答案 2 :(得分:0)
不,您不应该访问析构函数中的任何托管对象:这包括COM RCW。
相反,实现标准的IDisposable模式,并在Dispose(bool)方法中释放COM对象,就像使用一次性托管对象一样。