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

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

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

在下面的示例代码(来自MSDN)中,“Component”是一个托管资源,我们称这个资源为dispose()(component.Dispose())。 我的问题是,我们如何实现这个方法的组件类是管理资源? 我们是否应该使用像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.
}
}

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

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

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

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


这种一次性模式令人困惑。 这是一个更好的实现方法:

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

public class NativeDisposable : IDisposable {

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

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

  ~NativeDisposable() {
    CleanUpNativeResource();
  }

  // ...

  IntPtr _nativeResource;

}

第二步:当这个类拥有其他一次性类时,创建一个可支配的类。 这很容易实现,你不需要它的终结器。 在您的Dispose方法中,只需在其他一次性产品上调用Dispose。 在这种情况下,您不关心非托管资源:

public class ManagedDisposable : IDisposable {

  // ...

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

  IDisposable _otherDisposable;

}

示例中的“组件”可以是其中之一,具体取决于它是封装非托管资源,还是仅由其他可用资源组成。

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


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

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

上一篇: How to dispose managed resource in Dispose() method in C#?

下一篇: doubts regarding Memory management in .net