仅用于受管资源的最小IDisposable启示

关于处理非托管资源的“标准完整” IDisposable实现有很多信息 - 但实际上这种情况非常罕见(大多数资源已被托管类包装)。 这个问题关注于IDisposable的一个简单实现,用于更常见的“仅管理资源”情况。

1:以下代码中IDisposable的最小实现是否正确,是否存在问题?

2:是否有任何理由添加完整的标准IDisposable实现( Dispose()Dispose(bool)Finalizer等)以提供最小的实现?

3:在这个最小的情况下可以/明智地使Dispose虚拟(因为我们不提供Dispose(bool) )?

4:如果这个最小的实现被包含一个(在这种情况下无用的)终结器的完整标准实现取代 - 这是否改变了GC如何处理对象? 有什么缺点吗?

5:示例包括Timer和事件处理程序,因为这些情况下,尤其重要的是不能错过的无法处理它们会保持对象活蹦乱跳( this在的情况下, TimereventSource事件处理程序的情况下),直到GC得到轮在他们的时间处理它们。 还有其他例子吗?

class A : IDisposable {
    private Timer timer;
    public A(MyEventSource eventSource) {
        eventSource += Handler
    }

    private void Handler(object source, EventArgs args) { ... }

    public virtual void Dispose() {
        timer.Dispose();
        if (eventSource != null)
           eventSource -= Handler;
    }
}

class B : A, IDisposable {
    private TcpClient tpcClient;

    public override void Dispose() {
        (tcpClient as IDispose).Dispose();
        base.Dispose();
    }   
}

裁判:
MSDN
SO:我什么时候需要管理托管资源
SO:如何在C#中的Dispose()方法中处理托管资源
SO:Dispose()用于清理管理资源


  • 实现是正确的,没有问题,只要派生类没有直接拥有非托管资源。

  • 实施完整模式的一个很好的理由是“最少突击的原则”。 由于MSDN中没有描述这种简单模式的权威性文档,因此维护开发人员可能会怀疑 - 即使您觉得有必要问StackOverflow :)

  • 是的,在这种情况下Dispose是虚拟的。

  • 如果Dispose被调用并且被正确实现(即调用GC.SuppressFinalize),那么不必要的终结器的开销可以忽略不计。

  • .NET Framework之外的绝大多数IDisposable类本身都是IDisposable因为它们拥有托管的IDisposable资源。 他们很少直接持有非托管资源 - 这种情况只有在使用P / Invoke访问未由.NET Framework公开的非托管资源时才会发生。

    因此,推广这种更简单的模式可能有一个很好的理由:

  • 在使用非托管资源的罕见情况下,它们应该封装在实现终结器(如SafeHandle)的密封IDisposable包装类中。 由于它是密封的,这个类不需要完整的IDisposable模式。

  • 在所有其他情况下,绝大多数情况下,可以使用更简单的模式。

  • 但除非微软或其他权威人士积极宣传它,否则我将继续使用完整的IDisposable模式。


    另一种选择是重构代码以避免继承,并使您的IDisposable类密封。 然后,更简单的模式很容易证明,因为支持可能继承的尴尬回转不再是必要的。 我个人大多数时候都采取这种方法; 在极少数情况下,我想要制作一个非密封类的一次性,我只是遵循'标准'模式。 培养这种方法的一个好处是,它倾向于将你推向组合而不是继承,这通常会使代码更易于维护和测试。


    我推荐的Dispose模式是用于非虚拟Dispose实现链接到虚拟void Dispose(bool) ,最好是类似于:

    int _disposed;
    public bool Disposed { return _disposed != 0; }
    void Dispose()
    {
      if (System.Threading.Interlocked.Exchange(ref _disposed, 1) != 0)
        Dispose(true);
      GC.SuppressFinalize(this); // In case our object holds references to *managed* resources
    }
    

    即使多个线程同时调用Dispose(bool) ,也只能调用一次。 虽然这种同时处置尝试很少(*),但防范它们很便宜; 如果基类不做类似上面的事情,那么每个派生类都必须有自己的冗余双重保护逻辑,并且可能还有一个冗余标志。

    (*)一些通常是单线程的并使用阻塞I / O的通信类允许从任何线程上下文中调用Dispose来取消阻塞自己线程的I / O操作[显然, Dispose不能被调用线程,因为该线程在阻塞时不能执行任何操作]。 这是完全可能的-而不是不合理的-这样的对象,或者其封装对象,有一个外螺纹尝试Dispose它们作为只是他们所要去的那一刻中止其当前操作的手段,以通过他们的主要处置线。 同时Dispose呼叫可能很少,但是它们的可能性并不表示任何“设计问题”,只要Dispose代码可以对一个呼叫采取行动而忽略另一个呼叫。

    链接地址: http://www.djcxy.com/p/54495.html

    上一篇: Minimal IDisposable implimenation for managed resources only

    下一篇: Implementing IDisposable correctly