【发布时间】:2021-04-09 08:27:22
【问题描述】:
以下是来自以下来源的代码: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose
using System;
class BaseClass : IDisposable
{
// To detect redundant calls
private bool _disposed = false;
~BaseClass() => Dispose(false);
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
// TODO: dispose managed state (managed objects).
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
_disposed = true;
}
}
消息来源说了一些为什么
if (disposing) {
// TODO: dispose managed state (managed objects).
}
需要:
如果方法调用来自终结器,则只有释放非托管资源的代码应该执行。实现者负责确保错误路径不会与可能已被回收的托管对象交互。这很重要,因为垃圾收集器在终结期间销毁托管对象的顺序是不确定的。
但是我有一个问题,当 BaseClass 的 Finalize 方法被调用时,它所有包含托管对象的字段仍将在堆中,因为 BaseClass 对象本身必须在垃圾收集中幸存下来并被提示到另一代,以及何时可终结的对象得到提升,其字段引用的任何对象也得到提升,因为它们也必须继续存在。
所以BaseClass的字段引用的托管对象不会被GC回收。谁能提供一个具体的例子为什么需要以这种方式实现?
【问题讨论】:
-
因为终结器在单独的线程上运行,并且终结器可能需要任何时间。 GC 将对象提升到下一代以使其“不碍事”并继续进行 GC,即使终结器可能仍在运行。这确实意味着子对象也可以存活,是的——终结器成本很高,应该保持在最低限度。理想情况下,可终结对象根本没有子对象,仅表示非托管资源(如句柄)。
-
只是在@JeroenMostert 所说的内容上添加一个额外的注释:很多的这种混乱来自人们试图在一个地方做太多事情,尤其是在涉及继承时;而不是在某些子类中添加非托管资源,更容易支持的方法是编写一个类,其中存在的唯一原因是管理该句柄/任何东西 - 可能是
sealed,带有非常简单的终结器和 Dispose - 然后在新的子类中引用到其他类型;继承模型中的类型:永远不会获得终结器,并且具有简单的 Dispose。