【问题标题】:Why the IDispose pattern is implemented in this way?为什么IDispose模式是这样实现的?
【发布时间】: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。

标签: c# .net


【解决方案1】:

如果你在一个finalizer(而不是Dispose())中——即disposingfalse,你根本不应该假设任何关于陈述其他对象。 您不知道终结器的执行顺序。没有确定的顺序,也无法提供(想想“周期”)。此外:这不是重点。如果您想清理托管资源,您应该在Dispose() 中完成。如果有人没有调用Dispose(),那么那个人有一个错误,而不是你。您应该只与终结器中的 非托管 资源对话。坦率地说,访问真正不受管理的资源难以置信很少见,因此实际上:您几乎不需要任何这种复杂性。

【讨论】:

  • 但是当实例的Finalize方法运行时所有字段对象都在堆中,我不假设这是他的规则,你能提供一个具体的代码示例,谢谢
  • @slowjams 他们可能还在堆上,但你不知道他们还活着——他们现在是不死生物,如果 80 年代的恐怖节目被教导我什么都行:你不惹不死生物。它们可能已经完成,但你没有抓住重点:你不应该尝试触摸它们。它们现在处于未定义状态。我已经提供了一个具体的代码示例,它不接触终结器中的其他对象,但事实证明“不做某事”需要零行代码。你是在断言他们还活着并且很好:你不知道这是真的,也不应该假设它。
  • @slowjams 如果你的意思是“内存还没有被回收”,那很好 - 但这并没有改变任何事情:你不应该看 . 唯一 你应该在终结器中做的事情是释放 current 实例拥有的非托管资源。没有其他的。我还允许在 #if DEBUG 构建中出于调试目的使用静态日志记录和静态计数器(互锁)递增/递减。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-31
  • 2011-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多