C# 仍在尝试理解dispose模式

C# 仍在尝试理解dispose模式,c#,dispose,C#,Dispose,我已经阅读了msdn和各种关于dispose模式的帖子,但仍然有一些事情我不明白。我编写了以下代码来测试dispose模式。请注意,没有未管理的资源,我使用的是vs2008和.net 3.5: public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void tryDispose() { //test 1 :

我已经阅读了msdn和各种关于dispose模式的帖子,但仍然有一些事情我不明白。我编写了以下代码来测试dispose模式。请注意,没有未管理的资源,我使用的是vs2008和.net 3.5:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void tryDispose()
    {
        //test 1 : allocate resource and leave manage it to gc
        BL.myclass df = new BL.myclass();
        //test 2 : try to force garbage collecting
        //GC.Collect();
       //test 3 : call dispose myself
       //using (BL.myclass df = new BL.myclass())
        //{ 

        //}

    }

    private void button1_Click(object sender, EventArgs e)
    {
        tryDispose();
    }
这是我的一次性课程:

class myclass: IDisposable
{

    private StronglyTypedDs myDS;
    private bool _disposed;

    public myclass()
    {
        using (myDSTableAdapter docDocadpt = new myDSTableAdapter())
        {
            myDS = new StronglyTypedDs();
            docDocadpt.Fill(myDS.TheTable);
        }
    }


    #region IDisposable Members

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

    ~myclass()
    {
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                if (myDS != null)
                    myDS .Dispose();
                myDS = null;
            }
        }
        _disposed = true;
    }


    #endregion

}
结果是:

测试1a-仅实例化myclass,析构函数是commentend,因为myclass不包含非托管资源:即使关闭应用程序(而是执行其dispose),也不会调用myclass.dispose。那么,一旦我关闭应用程序,数据集的状态是什么

测试1b-取消对析构函数的注释,当我关闭应用程序时调用所有disposes是可以的

测试2a和2b-我只调用gc.collect进行上述测试:行为分别与测试1a和1b相同

测试3-一切正常(当然)


许多帖子说,如果myclass不包含非托管资源,我就不需要添加析构函数;但是在我的测试中,如果我不添加析构函数,那么当我关闭应用程序时,myclass.dispose不会被调用。我没有等待gc自己运行(如果我记得正确的话,gc.collect不能保证类实例被释放)来检查它是否会调用myclass.dispose。 那么,正确的实现是什么:如果myclass只包含托管资源,请始终定义e析构函数或避免它? 如果我已经填充了所有代的级别,那么在没有实现析构函数的情况下,gc是否会调用myclass dispose

最后我注意到,如果我定义了一个析构函数,但没有将该类声明为实现IDisposable,那么处理链仍然可以工作。这可能是有意义的,因为析构函数可能在IL中被翻译成finalize。但我发现它确实令人困惑:它是某种我不知道的“隐式”接口实现吗?gc可以处理该项,但用户不能

先谢谢你


Stefano

正确的模式是仅在类包含非托管资源时使用终结器。至于依靠
GC
来处置托管对象,请不要这样做。
IDisposable
合同明确说明需要处理此对象。

IDisposable的主要目的是提供一个一致的标准接口,您可以使用该接口处理非托管资源,也就是说,如果您没有采取措施确保调用dispose(),这些资源将在应用程序关闭后挂起。using()语法也可以理解,即using将为您实现以下块:

DisposableType someDisposable = new DisposableType();
try
{
    // Do whatever
}
finally
{
    ((IDisposable)someDisposable).Dispose();
}
这一切都是在一个很好的设计中实现的,如下所示:

using(DisposableType someDisposable = new DisposableType())
{
    // Do whatever
}

相信你的垃圾收集器。最终,托管资源将得到清理。除非您有一些需要释放的外部资源,否则不需要终结器或实现IDisposable

这通常意味着您仅应在以下情况下实现IDisposable:

  • 正在包装本机资源。(在本例中,您可能还需要一个终结器。)
  • 正在封装一个实现
    IDisposable
    的类。(在这种情况下,您需要
    IDisposable
    ,但不需要实现终结器/析构函数。)

  • 在我看来,与
    IDisposable
    混淆的部分原因在于它涵盖了相当多的用例,并且正确的实现会根据您所做的事情而变化(即:包装本机资源、包装其他IDisposable类或使用分解类型)。为了解决这个问题,我写了一个-它可能有助于为您澄清其中的一些问题。

    好的,我想我已经理解了,参考我的示例,实现dispose是正确的,因为数据集对于我的类是全局的,并且实现了IDisposable,而我不需要终结器,因为没有非托管资源。 即使我“忘记”在我的类dispose方法中处理一些托管资源,gc也会在某个时候收集它。dispose方法只是我为托管资源的其他类/开发人员提供的一个实用工具,如果我包装非托管资源,则必须使用终结器

    我将尽快阅读您提供的文章,但同时我还有最后一个问题:gc何时释放我的类及其资源所拥有的内存?当有人调用dispose或它将运行时(它将释放内存而不是将其移动到下一代)


    感谢大家的回答和示例

    您的代码是正确的,您实现的代码与MSDN库中的文档完全相同

    不过你需要再看看。解释析构函数(也称终结器)运行时发生的情况。disposing参数将为false,受保护的Dispose方法不执行任何操作。这是完全正常的,终结器应该只释放非托管资源。你没有。在您自己的代码中拥有非托管资源是非常罕见的。它们属于.NET中可用的尼斯包装类,用于将非托管操作资源转换为尼斯托管类。如果你发现自己需要一个终结器,那么99.99%的时候你都是错的。即使您包装了非托管资源,也应该使用一个SafeHandle包装器。依靠他们的终结器

    好吧,你想摆脱毁灭者。将对象留在内存中是不健康的,它会使对象在内存中保留的时间过长。当您这样做时,您会将其缩减为:

    public void Dispose()
    {
        if (myDS != null) myDS.Dispose();
    }
    
    这是大多数Dispose()方法的实现,只需调用类成员的Dispose方法即可。如果没有Dispose()方法的任何成员,则可以完全忽略它

    接下来,您确实误解了Dispose()方法的调用方式。它不是自动的。首先,自动释放资源是由垃圾收集器负责的,这就是它的意义所在。Dispose()方法供您调用,可以使用