【问题标题】:Should IDisposable::Dispose() be virtualIDisposable::Dispose() 是否应该是虚拟的
【发布时间】:2014-04-06 14:31:08
【问题描述】:

说 SomeDisposable 的工厂实际上是在创建/返回一种看门狗 Wrapper

public class Wrapper : SomeDisposable
{
    public new /*:(*/ Dispose() { ... };
}

调用者使用like

using (SomeDisposable sd = SomeDisposableFactory.Create(...))
{
} // Wrapper.Dispose() never called.

Wrapper.Dispose() 永远不会被调用。如果Dispose() 是虚拟的,那么Wrapper.Dispose() 将被调用。

IDisposable 接口不保证其他最佳实践方法virtual Dispose(bool) 确实存在或强制其是虚拟的,因此通常不能依赖它存在(这只是推荐的模式)。接口目前不允许对虚拟进行约束。

不将推荐的Dispose() 模式设为虚拟有哪些优点和缺点,这可以解决这个特殊的困境。 C# 是否应该允许通过接口强制使用虚拟方法(因为抽象类不像契约定义那样流行)。

【问题讨论】:

标签: c# .net dispose idisposable virtual-functions


【解决方案1】:

没有。该模式实际上表示Dispose()(非虚拟)应该调用protected virtual void Dispose(bool) 方法。这保证了基类Dispose 调用可以正确地向上传递层次结构。

IDisposable 的文档中有详细说明:

  • 它应该提供一个公共的、非虚拟的 Dispose() 方法和一个受保护的虚拟 Dispose(Boolean disposing) 方法。
  • Dispose() 方法必须调用 Dispose(true) 并且应该禁止完成以提高性能。
  • 基本类型不应包含任何终结器。

【讨论】:

  • 接受这个答案,同时希望文档中的“应该”和“必须”由 C# 本身指定/强制执行。
【解决方案2】:

这已经解决了。

sealed的一次性类型应该使用通用的dispose pattern

public class DisposableResourceHolder : IDisposable
{
    private SafeHandle resource; // handle to a resource

    public DisposableResourceHolder()
    {
        this.resource = ... // allocates the resource
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // dispose managed resources.
            if (resource != null) resource.Dispose();
        }

        // free unmanaged resources.
    }
}

【讨论】:

  • 我经常看到这个。这鼓励人们在不一定需要的地方使用终结器。您应该解释只有在类具有非托管资源时才应使用终结器。添加终结器时,您会自动使该对象成为长期存在的对象,并且在垃圾收集器中始终将其提升到至少 1 级生成对象。
  • @stevethethread 好点。如果需要,派生类总是可以定义自己的终结器。
  • @stevethethread 你的观点可能是正确的,但如果我没有非托管资源,我为什么要实现IDisposable
  • 托管资源。例如。数据库连接等。终结器的目的是确保在未调用 dispose 时清理未管理的资源。因此,为什么在 dispose 方法中调用 GC.SuppressFinalize,因为这意味着 Dispose 已被调用并且资源已被清理。
  • @stevethethread: 对GC.SuppressFinalize() 的调用可能并不是真正需要的,因为唯一应该定义清理终结器的类是那些直接从System.Object 派生的类,但在某些情况下它可能是有必要确保对Dispose 的调用无法优化; GC.KeepAlive(this) 是一个很好的方法,但 GC.SuppressFinalize(this) 也一样好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-29
  • 2015-09-24
  • 2022-06-10
  • 1970-01-01
  • 1970-01-01
  • 2014-10-18
相关资源
最近更新 更多