是的,您当然可以使用终结器中的字符串以及许多其他对象类型。
对于所有这一切的确切来源,我会去拿起 Jeffrey Richter 写的书CLR via C#, 3rd edition。在第 21 章中对此进行了详细描述。
无论如何,这就是真正发生的事情......
在垃圾回收期间,任何具有终结器但仍希望被调用的对象都被放置在一个特殊的列表中,称为 freachable 列表。
这个列表被认为是一个根,就像静态变量和实时局部变量一样。因此,那些对象引用的任何对象,以此类推,这一次都从垃圾回收周期中移除。 它们将在当前的垃圾收集周期中存活下来,就好像它们一开始就没有资格收集一样。
请注意,这包括字符串,这是您的问题,但它也涉及所有其他对象类型
然后,在稍后的某个时间点,终结器线程从该列表中获取对象,并对这些对象运行终结器,然后将这些对象从该列表中移除。
然后,下次垃圾回收运行时,它会再次找到相同的对象,但这一次终结器不再想运行,它已经执行,所以对象被正常回收。
在我告诉你什么不起作用之前,让我用一个例子来说明。
假设您有对象 A 到 Z,并且每个对象都引用下一个对象,因此您有对象 A 引用对象 B,B 引用 C,C 引用 D,依此类推,直到 Z。
其中一些对象实现了终结器,并且它们都实现了 IDisposable。让我们假设 A 没有实现终结器,但 B 实现了,然后其余的一些也实现了,对于这个超出 A 和 B 的示例来说并不重要。
您的程序保留了对 A 的引用,并且只有 A。
在一个普通且正确的使用模式中,您将处理 A,处理 B,处理 C,等等。但是您有一个错误,所以这不会发生。在某个时候,所有这些对象都有资格被收集。
此时 GC 会找到所有这些对象,但随后注意到 B 有一个终结器,它还没有运行。因此,GC 会将 B 放在 freachable 列表中,并递归地从 GC 列表中取出 C、D、E 等直到 Z,因为 B 突然变为 in- em> 有资格收集,其余的也是如此。请注意,其中一些对象本身也被放置在 freachable 列表中,因为它们自己有终结器,但所有它们引用的对象将在 GC 中存活。 p>
但是,A 被收集了。
让我把上面的段落说清楚。此时,A 已被收集,但 B、C、D 等直到 Z 还活着,好像什么都没发生过。虽然你的代码不再引用它们中的任何一个,但freachable列表有。
然后,终结器线程运行,并终结 freachable 列表中的所有对象,并将对象从列表中移除。
下次运行 GC 时,现在会收集这些对象。
所以这肯定行得通,那么大吵闹是怎么回事?
问题出在终结器线程上。该线程不假设它应该完成这些对象的顺序。它不这样做是因为在许多情况下它不可能这样做。
正如我上面所说,在一个普通的世界中,你会在 A 上调用 dispose,它会释放 B,它会释放 C,等等。如果这些对象之一是流,则引用流的对象可能会在调用 Dispose 时,说“我会在处理流之前继续刷新我的缓冲区。”这是完全合法的,很多现有的代码都是这样做的。
但是,在终结线程中,不再使用此顺序,因此如果将流放在引用它的对象之前的列表中,则流在引用它的对象之前被终结并因此关闭。
也就是说,你不能做的事情总结如下:
您无法访问对象引用的任何具有终结器的对象,因为您无法保证在终结器运行时这些对象将处于可用状态。 对象仍会在内存中那里,并且不会被收集,但它们可能已经被关闭、终止、终结等。
所以,回到你的问题:
问。我可以在终结器方法中使用字符串吗?
A. 是的,因为字符串不实现终结器,并且不依赖具有终结器的其他对象,因此在终结器运行时将处于活动状态并开始运行。
让你走错路的假设是问题的第二句话:
在终结器中我不能使用其他对象,因为它们可能已经被垃圾回收了。
正确的句子是:
在终结器中我不能使用其他具有终结器的对象,因为它们可能已经被终结了。
举个例子,终结器无法知道正确终结两个对象的顺序,考虑两个相互引用并且都具有终结器的对象。终结器线程必须分析代码以确定它们通常以什么顺序被处理,这可能是两个对象之间的“舞蹈”。终结器线程不这样做,它只是在另一个之前终结一个,你无法保证哪个是第一个。
那么,是否有任何时候安全可以从我自己的终结器中访问也具有终结器的对象?
唯一保证安全的情况是当您的程序/类库/源代码拥有这两个对象时,您就知道它是。
在我解释之前,这不是很好的编程习惯,所以你可能不应该这样做。
例子:
您有一个对象Cache,它将数据写入文件,该文件永远不会保持打开状态,因此只有在对象需要向其写入数据时才会打开。
您有另一个对象CacheManager,它使用第一个对象,并调用第一个对象为其提供数据以写入文件。
CacheManager 有一个终结器。这里的语义是,如果管理器类被收集,但没有被释放,它应该删除缓存,因为它不能保证它们的状态。
但是,缓存对象的文件名可以从缓存对象的属性中获取。
所以问题是,我是否需要将该文件名复制到管理器对象中,以避免在完成过程中出现问题?
不,你没有。当管理器最终确定时,缓存对象仍在内存中,它所引用的文件名字符串也是如此。但是,您不能保证缓存对象上的任何终结器尚未运行。
但是,在这种情况下,如果您知道缓存对象的终结器不存在,或者没有触及文件,那么您的管理器可以读取缓存的文件名属性对象,然后删除文件。
但是,由于您现在在这里有一个非常奇怪的依赖关系,我当然建议不要这样做。