如何在C#中的Dispose()方法中配置托管资源?

时间:2010-02-17 21:45:52

标签: c# .net garbage-collection dispose resources

我知道Dispose()适用于非托管资源,不需要等待垃圾收集器完成对象时,应该在不再需要时处理资源。

但是,在处理对象时,它会抑制垃圾收集器的完成(GC.SuppressFinalize(this);在下面的代码中)。这意味着如果对象包含托管资源,我们也必须照顾它,因为垃圾收集器不会清理它。

在下面的示例代码中(来自MSDN),“Component”是一个托管资源,我们为此资源调用dispose()(component.Dispose())。我的问题是,我们如何为作为托管资源的Component类实现此方法?我们应该使用像Collect()这样的东西来捅垃圾收集器来清理这部分吗?

任何想法都会受到赞赏。感谢。

以下是我正在查看的代码来自MSDN:

using System;
using System.ComponentModel;

// The following example demonstrates how to create
// a resource class that implements the IDisposable interface
// and the IDisposable.Dispose method.

public class DisposeExample
{
// A base class that implements IDisposable.
// By implementing IDisposable, you are announcing that
// instances of this type allocate scarce resources.
public class MyResource: IDisposable
{
    // Pointer to an external unmanaged resource.
    private IntPtr handle;
    // Other managed resource this class uses.
    private Component component = new Component();
    // Track whether Dispose has been called.
    private bool disposed = false;

    // The class constructor.
    public MyResource(IntPtr handle)
    {
        this.handle = handle;
    }

    // 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.
                component.Dispose();
            }

            // Call the appropriate methods to clean up
            // unmanaged resources here.
            // If disposing is false,
            // only the following code is executed.
            CloseHandle(handle);
            handle = IntPtr.Zero;

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

        }
    }

    // Use interop to call the method necessary
    // to clean up the unmanaged resource.
    [System.Runtime.InteropServices.DllImport("Kernel32")]
    private extern static Boolean CloseHandle(IntPtr handle);

    // 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.
    ~MyResource()
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }
}
public static void Main()
{
    // Insert code here to create
    // and use the MyResource object.
}
}

4 个答案:

答案 0 :(得分:15)

那种一次性模式是confusing。这是实现它的a better way

步骤1。创建一个一次性类来封装您拥有的每个非托管资源。这应该是非常罕见的,大多数人没有非托管资源来清理。这个类only cares (pdf)关于它的非托管资源,应该有一个终结器。实现如下:

public class NativeDisposable : IDisposable {

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

  protected virtual void CleanUpNativeResource() {
    // ...
  }

  ~NativeDisposable() {
    CleanUpNativeResource();
  }

  // ...

  IntPtr _nativeResource;

}

第2步。当班级持有其他一次性课程时,创建一个一次性班级。这很容易实现,你不需要终结器。在Dispose方法中,只需在其他一次性用品上调用Dispose。在这种情况下,您不关心非托管资源:

public class ManagedDisposable : IDisposable {

  // ...

  public virtual void Dispose() {
    _otherDisposable.Dispose();
  }

  IDisposable _otherDisposable;

}

示例中的“Component”可以是其中之一,具体取决于它是否封装了非托管资源,还是仅包含其他可支配资源。

另请注意,压制终结并不意味着您要阻止垃圾收集器清理您的实例;它只是意味着当垃圾收集器在你的实例中运行时,它不会调用为它定义的终结器。

答案 1 :(得分:11)

  

这意味着如果对象包含托管资源,我们也必须处理它,因为垃圾收集器不会清理它。

这是错误的。垃圾收集器仍将清理您的托管资源。终结器也严格用于清理非托管资源,因此SuppressFinalize()调用不会对您造成伤害。

由于你是IDisposable模式的新手,我会预料到你的下一个困惑点:写终结者。在C#中,只应在处理全新的非托管资源时编写终结器。因此,如果你有一个将System.Data.SqlClient.SqlConnection类型包装为数据访问层一部分的类,你应该为该类型编写终结器,因为你仍然处理相同类型的底层非托管资源:sql server数据库连接。该资源的终结器已由基本SqlConnection类型处理。

另一方面,如果您正在为一种全新的数据库引擎构建ADO.Net提供程序,则需要在连接类中实现终结器,因为之前从未进行过。

答案 2 :(得分:1)

也许更清楚一点。 GC.SuppressFinalize(this)仅影响this指针引用的对象,但不影响对象的任何成员。也就是说,SuppressFinalize不会递归地应用于对象的成员。当垃圾收集器回收Disposed对象的内存时,很可能没有对对象字段的活动引用。由于您没有在对象的所有字段上调用GC.SuppressFinalize,因此垃圾收集器将在这些对象上调用finalize方法(如果存在)。当它完成时,这完全取决于运行时,通常你应该让它做它的事情。

答案 3 :(得分:0)

很抱歉,如果我误解了你的问题!!,但是如果你的类只引用了其他托管类,并且那些对这些对象的引用不需要处理,那么你的类不一定需要实现IDisposable。