C#如何实现Dispose方法

时间:2011-09-20 12:23:15

标签: c# dispose

我需要一些关于Dispose方法实现的建议。

在我们的应用程序中,用户设计自己的UI。我有一个预览窗口,显示UI的外观。在此UI中绘制的所有对象最终都来自公共基类ScreenObject。我的预览管理器包含对ScreenGrid的单个对象引用,ScreenGrid是整个预览区域的网格对象。

问题#1

我的一些派生屏幕类保留了非托管资源,例如数据库连接,位图图像和WebBrowser控件。这些类需要处理这些对象。我在基本Dispose基类中创建了一个虚拟ScreenObject方法,然后在每个派生类中实现了覆盖非托管资源的覆盖Dispose方法。但是,现在我刚刚创建了一个名为Dispose的方法,我没有实现IDisposable。我应该实施IDisposable吗?如果是这样,我该如何实现呢?

  • 仅限具有非托管资源的派生类
  • 具有非托管资源的基类和派生类 OR
  • 基类和所有派生类,包括那些没有非托管资源的类

将虚拟Dispose方法放在没有非托管资源的基类中是否错误,以便您可以利用多态?

问题#2

在阅读Dispose方法和IDisposable接口时,Microsoft声明处置对象应该只为其父级调用Dispose方法。父母将为其父母调用它,依此类推。对我而言,这似乎是倒退。我可能想要处理一个孩子但保留其父母。

我认为应该是另一种方式,被处置的物品应该处理它的孩子。然后孩子们应该处理他们的孩子等等。

我在这里错了还是我错过了什么?

3 个答案:

答案 0 :(得分:34)

问题1:使用以下模式实施IDisposable

public class MyClass : IDisposable
{
    bool disposed;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                //dispose managed resources
            }
        }
        //dispose unmanaged resources
        disposed = true;
    }

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

问题2:Microsoft的含义是派生类在其父类上调用dispose。实例的所有者仅在最派生类型上调用Dispose。

一个(缩短的)例子:

class Parent : IDisposable 
{
    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                //dispose managed resources
            }
        }
        //dispose unmanaged resources
        disposed = true;
    }

}
class Child : Parent, IDisposable 
{ 
    protected override void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                //dispose managed resources
            }
            base.Dispose(disposing);
        }
        //dispose unmanaged resources
        disposed = true;
    }

}
class Owner:IDisposable
{
    Child child = new Child();
    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                if(child!=null)
                {
                    child.Dispose();
                }
            }
        }
        //dispose unmanaged ressources
        disposed = true;
    }
}

所有者仅在Child上调用Dispose,而在Parent上调用Dispose。 Child负责在Parent上调用{{1}}。

答案 1 :(得分:7)

问题1:

根据您列出的对象类型(即数据库,WebBrowser,位图等),就.NET而言,这些只是托管资源。因此,您应该在具有一次性类型作为成员的任何类上实现IDisposable。如果它们是本地声明的实例,则只需对它们调用'using()'。虽然你提到的这些实例确实有非托管资源,但是这些实际上是通过.NET从你使用的类型中抽象出来的。由于您只使用托管类型,因此应实施IDisposable 但不使用终结工具。如果您真的拥有非托管资源作为类成员,则只需要实现终结器。

问题2:

您似乎将继承(是a)与聚合/包含(具有a)混淆。例如,如果“Container”包含一次性资源作为类成员,则称为聚合/包含。因此,在Container的base.Dispose()实现中调用IDisposable与处理Container内部的 无关。你应该记住,如果一个类派生自Container,比如说“DerivedContainer”,它 Container的一个实例,尽管有其他成员和/或功能。因此,“DerivedContainer”的任何实例都具有其基类“Container”所具有的所有成员。如果你从未调用base.Dispose(),那么“容器”中的可支配资源将无法正常发布(实际上它将由GC发布,但由于许多原因,只是'让.NET处理它'是不好的做法) - 请参阅我在 Is it bad practice to depend on the .NET automated garbage collector? 上发布的答案。

如果你没有调用基类Dispose(),你最终会得到一个部分处理的对象(放置在派生类中但不在基类中) - 这是一个非常糟糕的场景。因此,调用基类Dispose()非常重要。

我在我的博客上编写了一个我已经开发的最佳实践模式(具有大量经验和调试内存转储)作为示例。它显示了如何在基类和派生类上实现IDisposable

<强> http://dave-black.blogspot.com/2011/03/how-do-you-properly-implement.html

答案 2 :(得分:1)

  

我实现了IDisposable

 class ConnectionConfiguration:IDisposable
{
    private static volatile IConnection _rbMqconnection;
    private static readonly object ConnectionLock = new object();
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected virtual void Dispose(bool disposing)
    {
        if (!disposing)
        {
            return;
        }
        if (_rbMqconnection == null)
        {
            return;
        }
        lock (ConnectionLock)
        {
            if (_rbMqconnection == null)
            {
                return;
            }
            _rbMqconnection?.Dispose();//double check
            _rbMqconnection = null;
        }
    }
}