处理,何时被称为?

时间:2010-05-20 07:53:28

标签: c# .net

请考虑以下代码:

namespace DisposeTest
{
    using System;

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Calling Test");

            Test();

            Console.WriteLine("Call to Test done");
        }

        static void Test()
        {
            DisposeImplementation di = new DisposeImplementation();
        }
    }

    internal class DisposeImplementation : IDisposable
    {
        ~DisposeImplementation()
        {
            Console.WriteLine("~ in DisposeImplementation instance called");
        }
        public void Dispose()
        {
            Console.WriteLine("Dispose in DisposeImplementation instance called");
        }
    }
}

即使我在Test();调用之后放置了一个等待循环,Dispose也永远不会被调用。所以这很糟糕。我想编写一个简单易用的类,以确保清理所有可能的资源。我不想把这个责任交给我班上的用户。

可能的解决方案:使用using,或者调用Dispose我自己(基本相同)。我可以强制用户使用吗?或者我可以强制调用处理吗?

GC.Collect();之后调用Test();也不起作用。

di置于null也不会调用Dispose。解构函数可以工作,因此当对象退出时会被解构Test()

好的,现在很清楚了!

谢谢大家的回答!我会在评论中添加警告!

7 个答案:

答案 0 :(得分:46)

应该提出几个要点来解决OP的问题:

  1. .NET GC是非确定性的(即你永远不知道也不应该依赖它发生的时间)
  2. .NET Framework永远不会调用Dispose;您必须手动调用它 - 最好将其创建包装在using()块中。
  3. 将一次性对象显式设置为null而不调用Dispose()是件坏事。发生的是您将对象“root reference”显式设置为null。这实际上意味着您以后不能调用Dispose,更重要的是,它将对象发送到GC Finalization Queue for Finalization。应该不惜一切代价避免因糟糕的编程习惯而导致终结。
  4. <强>终结: 一些开发人员将其称为析构函数。 事实上它甚至在当前C# 4.0 Language Spec (section 1.6.7.6) ECMA-334 spec之前的版本中称为析构函数。幸运的是,第4版(2006年6月)正确定义了第8.7.9节中的终结器,并尝试在第17.12节中澄清两者之间的混淆。应该注意的是,在.NET Framework中,传统上称为析构函数和析构函数/终结符之间存在重要的内部差异(无需在此处进行详细介绍)。

    1. 如果存在Finalizer,那么当且仅当未调用GC.SuppressFinalize()时,.NET Framework才会调用它。
    2. 你永远不应该明确地召唤终结者。幸运的是,C#不会明确允许这个(我不知道其他语言);虽然可以通过为第二代GC调用GC.Collect(2)来强制它。
    3. <强>最后确定: 最终化是.NET Framework处理“优雅”清理和释放资源的方法。

      1. 只有在Finalization Queue中有对象时才会发生。
      2. 仅当Gen2发生垃圾收集时才会发生(对于编写良好的.NET应用程序,每100个集合中 1)。
      3. 直到并包括.NET 4,都有一个Finalization线程。如果此线程因任何原因而被阻止,则您的应用程序会被屏蔽。
      4. 编写正确且安全的终结代码并非易事,并且可以很容易地犯错(即,意外地允许从Finalizer中抛出异常,允许依赖于已经完成的其他对象等)。
      5. 虽然这肯定是您要求的更多信息,但它提供了有关工作方式以及工作原理的背景知识。有些人会争辩说他们不应该担心在.NET中管理内存和资源,但这并没有改变它需要完成的事实 - 我不认为它会在不久的将来消失。

答案 1 :(得分:19)

  

我想写一个类   简单易用   确保每一个可能的   资源被清理干净。我不想要   把责任交给用户   我班上的。

你做不到。内存管理根本不是为了容纳非特定内存的资源而构建的。

IDisposable模式旨在让开发人员在完成对象时告诉对象,而不是让内存管理通过使用引用计数之类的东西来解决这个问题。

您可以将Finalizer用作未能正确处理对象的用户的后备,但它不能很好地作为清理对象的主要方法。为了顺利工作,应妥善处理对象,这样就不需要调用更昂贵的终结器。

答案 2 :(得分:13)

所有答案都是(或多或少)正确,这是一个例子:

static void Test()
{
    using (DisposeImplementation di = new DisposeImplementation())
    {
        // Do stuff with di
    }
}

手动调用Dispose也会有效,但using语句的优点是当你离开控制块时也会处理该对象,因为抛出了异常。

您可以添加一个处理资源处理的终结器,以防有人“忘记”使用IDisposable接口:

public class DisposeImplementation : IDisposable
{    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }   
        // get rid of unmanaged resources
    }

    ~DisposeImplementation()
    {
        Dispose(false);
    }
}

有关其他信息,请参阅this question。但是,这只是补偿了没有正确使用你的课程的人:)我建议你给Finalizer添加一个很大的Debug.Fail()调用,以警告开发者他们的错误。

如果您选择实施该模式,您会看到GC.Collect()将触发处置。

答案 3 :(得分:7)

将其用作课程的模式/模板

public class MyClass : IDisposable
{
    private bool disposed = false;

    // Implement IDisposable.
    // Do not make this method virtual.
    // A derived class should not be able to override this method.
    public void Dispose()
    {
        Dispose(true);
        // This object will be cleaned up by the Dispose method.
        // Therefore, you should call GC.SupressFinalize to
        // take this object off the finalization queue
        // and prevent finalization code for this object
        // from executing a second time.
        GC.SuppressFinalize(this);
    }

    // Dispose(bool disposing) executes in two distinct scenarios.
    // If disposing equals true, the method has been called directly
    // or indirectly by a user's code. Managed and unmanaged resources
    // can be disposed.
    // If disposing equals false, the method has been called by the
    // runtime from inside the finalizer and you should not reference
    // other objects. Only unmanaged resources can be disposed.
    private void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called.
        if (!this.disposed)
        {
            // If disposing equals true, dispose all managed
            // and unmanaged resources.
            if (disposing)
            {
                // Dispose managed resources.                
                ......
            }

            // Call the appropriate methods to clean up
            // unmanaged resources here.
            // If disposing is false,
            // only the following code is executed.
            ...........................

            // Note disposing has been done.
            disposed = true;
        }
    }

    // Use C# destructor syntax for finalization code.
    // This destructor will run only if the Dispose method
    // does not get called.
    // It gives your base class the opportunity to finalize.
    // Do not provide destructors in types derived from this class.
    ~MyClass()
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }
}

当然,正如其他人所提到的,不要忘记using(...){}阻止。

答案 4 :(得分:2)

您必须显式调用Dispose或将对象包装在using语句中。例如:

using (var di = new DisposeImplementation())
{
}
  

可能的解决方案:使用或呼叫   处理我自己(基本相同)。

使用using与调用Dispose块中的finally相同。

答案 5 :(得分:1)

你应该自己处理它,要么调用Dispose方法,要么使用using。记住,它不是一个解构者!

如果你不能相信你班级的用户正确处理资源,他们可能会以其他方式陷入困境。

答案 6 :(得分:1)

Dispose不会自动调用。您需要使用using子句来包装使用或手动调用它。

请参阅http://msdn.microsoft.com/en-us/library/aa664736%28VS.71%29.aspx

只是为了抢占你可能有的另一个想法:你不能从析构函数中调用dispose ......我前段时间在一个项目中试过这个。