正确实现IDisposable Dispose和Close的区别

正確實現IDisposable Dispose方法

using System;
using System.Collections.Generic;
using System.Text;

namespace NET.MST.Third.FinalizeDispose
{
public class FinalizeDisposeBase : IDisposable
{
// 标记对象是否已被释放
private bool _disposed = false;

// Finalize方法:析構函數
~FinalizeDisposeBase()
{
Dispose(
false);
}

// 这里实现了IDispose中的 Dispose方法
public void Dispose()
{
Dispose(
true);
//告诉GC此对象的Finalize方法不再需要调用
GC.SuppressFinalize(true);
}

//在这里做实际的析构工作
//申明为虚方法以供子类在有必要时重写
protected virtual void Dispose(bool isDisposing)
{
// 当对象已经被析构时,不再执行
if (_disposed)
return;
if (isDisposing)
{
//在这里释放托管资源
//只在用户调用Dispose方法时执行
}
//在这里释放非托管资源

//标记对象已被释放
_disposed = true;
}
}

public sealed class FinalizeDispose : FinalizeDisposeBase
{
private bool _mydisposed = false;

protected override void Dispose(bool isDisposing)
{
// Don't dispose more than once.
if (_mydisposed)
return;
if (isDisposing)
{
//在这里释放托管的并且在这个类型中申明的资源
}
//在这里释放非托管的并且在这个类型中申明的资源

//调用父类的Dispose方法来释放父类中的资源
base.Dispose(isDisposing);

// 设置子类的标记
_mydisposed = true;
}

static void Main()
{
}
}
}

在.NET的对象中实际上有两个用于释放资源的函数:Dispose和Finalize。Finalize的目的是用于释放非托管的资源,而Dispose是用于释放所有资源,包括托管的和非托管的。

在这个模式中,void Dispose(bool disposing)函数通过一个disposing参数来区别当前是被Dispose()调用还是被析构函数调用(当disposing为“true”时,说明Dispose()是被程序显示调用的,需要释放托管资源和非托管资源;当disposing为“false”时,说明Dispose()是被析构函数(也就是C#的Finalize())调用的,只需要释放非托管资源)。

这是因为,Dispose()函数是被其它代码在程序中显式调用并要求释放资源的,而Finalize是被GC调用的。在GC调用的时候MyResource所引用的其它托管对象可能还不需要被销毁,并且即使要销毁,也会由GC来调用。因此在Finalize中只需要释放非托管资源即可。另外一方面,由于在Dispose()中已经释放了托管和非托管的资源,因此在对象被GC回收时再次调用Finalize是没有必要的,所以在Dispose()中调用GC.SuppressFinalize(this)避免重复调用Finalize。

然而,即使重复调用Finalize和Dispose也是不存在问题的,因为有变量_disposed的存在,资源只会被释放一次,多余的调用会被忽略过去。

因此,上面的模式保证了:
1、 Finalize只释放非托管资源;
2、 Dispose释放托管和非托管资源;
3、 重复调用Finalize和Dispose是没有问题的;
4、 Finalize和Dispose共享相同的资源释放策略,因此他们之间也是没有冲突的。

在C#中,这个模式需要显式地实现,其中C#的析构函数~MyResource()代表了Finalize()。

关于资源释放,最后一点需要提的是Close函数。在语义上它和Dispose很类似,按照MSDN的说法,提供这个函数是为了让用户感觉舒服一点,因为对于某些对象,例如文件,用户更加习惯调用Close()。

那么Dispose和Close到底有什么区别?
当我们开发C#代码的时候,经常碰到一个问题,有些class提供Close(),有些class提供Dispose(),那么Dispose和Close到底有什么区别?

首先,Dispose和Close基本上应该是一样的。Close是为了那些不熟悉Dispose的开发者设计的。因为基本上所有的developer都知道Close是干吗的(特别是对于那些有C++背景的developer)。

但是当我们写code时候,如果要实现Close和Dispose的时候,要注意Close和Dispose的设计模式。.net的一些class只提供Close,而且派生自IDisposable,并且隐藏了Dispose方法。是不是觉得很不明白了?

对这些class来说,关键在于它们显式的(explicitly)实现了IDisposable。对于隐式实现来说,你只需要调用"new A().Dispose()",但是对于显式实现来说,Dispose不会是这个class的成员函数。唯一的调用方式是你先要cast到 IDisposable才行。(“new A().Dispose()”编译不过,但是“((IDisposable)new A()).Dispose()”可以编译过)。所以这样就符合了设计的要求:提供Close(),隐藏Dispose(),并且实现了 IDisposable接口。

在.net的framework里,Close()被设计成public的,并且在Close()里面call被隐藏的Dispose(); Dispose()去call另一个virtual的Dispose(bool)函数。所以如果你从这个class继承,你就必须实现Dispose (bool)方法。

调用者call Close()的时候就会call到你重载的那个Dispose(bool)方法去释放资源。

请参考 http://blogs.msdn.com/brada/archive/2003/07/06/50127.aspx

注意事项:
1,Close()不应该被定义成virtual。对于这个design pattern,Close()只是用来call那个隐藏的Dispose(),用户不应该改变Close的behavior。对于这个问题, System.IO.Stream也有设计问题。之所以有问题是为了满足向后兼容的需求。See http://msdn2.microsoft.com/en-us/library/ms227422.aspx. 文档里面提到虽然Close()是virtual的,但是不应该被override。

示例代碼:

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;

namespace ConsoleApplication
{
abstract class MyStream : IDisposable
{
//私有字段
private IntPtr m_unmanagedResource; // Unmanaged resource
private Bitmap m_bitmap; // IDisposable managed resources
private bool m_disposed;
//構造函數
public MyStream()
{
m_unmanagedResource
= Marshal.AllocCoTaskMem(100);
m_bitmap
= new Bitmap(50, 50);
}
//析構函數,代表Finalize()
~MyStream()
{
Dispose(
false);
}
// 这里实现了IDispose中的 Dispose方法
void IDisposable.Dispose()
{
Dispose(
true);
//告诉GC此对象的Finalize方法不再需要调用
GC.SuppressFinalize(true);
}
//在这里做实际的析构工作
//申明为虚方法以供子类在有必要时重写
protected virtual void Dispose(bool isDisposing)
{
// 当对象已经被析构时,不再执行
if (!m_disposed)
{
if (isDisposing)
{
//在这里释放托管资源
//只在用户调用Dispose方法时执行
m_bitmap.Dispose();
}
//在这里释放非托管资源
Marshal.FreeCoTaskMem(m_unmanagedResource);
//标记对象已被释放
m_disposed = true;
}
}

//显式的(explicitly)实现了IDisposable
public void Close()
{
((IDisposable)
this).Dispose();
}
}

/// <summary>
/// 子類,繼承自MyStream
/// </summary>
class MyDerivedStream : MyStream
{
// 私有字段
private IntPtr m_anotherMemory;
private Bitmap m_anotherImage;
private bool m_disposed;

//構造函數
public MyDerivedStream()
{
m_anotherMemory
= Marshal.AllocCoTaskMem(20);
m_anotherImage
= new Bitmap(24, 24);
}
//重載Dispose方法
protected override void Dispose(bool isDisposing)
{
if (!m_disposed)
{
if (isDisposing)
{
//在这里释放托管的并且在这个类型中申明的资源
m_anotherImage.Dispose();
}
//在这里释放非托管的并且在这个类型中申明的资源
Marshal.FreeCoTaskMem(m_anotherMemory);
//调用父类的Dispose方法来释放父类中的资源
base.Dispose(isDisposing);
m_disposed
= true;
}
}

public static void Main(string[] args)
{
MyStream aStream
= new MyDerivedStream();

aStream.Close();
// Allowed
// aStream.Dispose(); // Cannot compile

((IDisposable)aStream).Dispose();
// Allowed

//
// This one works as well, because newStream calls the explicit implemented
// IDisposable.Dispose method
//
using (MyStream newStream = new MyDerivedStream())
{
//
// Do something
//
}
}

}
}
posted @ 2011-02-15 13:43 Athrun 阅读(...) 评论(...) 编辑 收藏