编写高质量代码改善C#程序的157个建议——建议46:显式释放资源需继承接口IDisposable
建议46:显式释放资源需继承接口IDisposable
C#中的每一个类型都代表一种资源,资源分为两类:
托管资源:由CLR管理分配和释放的资源,即从CLR里new出来的对象。
非托管资源:不受CLR管理的对象,如Windows内核对象,或者文件、数据库连接、套接字、COOM对象等。
如果我们的类型使用了非托管资源,或者需要显示地释放托管资源,那么就需要让类型继承接口IDisposable,这毫无例外。这相当于告诉调用者,类型资源是需要显示释放资源的,你需要调用类型的Dispose方法。
一个标准的继承了IDisposable接口的类型应该像下面这样去实现,这种实现我们称为Dispose模式:
public class SampleClass : IDisposable { //演示创建一个非托管资源 private IntPtr nativeResource = Marshal.AllocHGlobal(100); //演示创建一个托管资源 private AnotherResource managedResource = new AnotherResource(); private bool disposed = false; /// <summary> /// 实现IDisposable中的Dispose方法 /// </summary> public void Dispose() { //必须为true Dispose(true); //通知垃圾回收机制不再调用终结器(析构器) GC.SuppressFinalize(this); } /// <summary> /// 不是必要的,提供一个Close方法仅仅是为了更符合其他语言(如 /// C++)的规范 /// </summary> public void Close() { Dispose(); } /// <summary> /// 必须,防止程序员忘记了显式调用Dispose方法 /// </summary> ~SampleClass() { //必须为false Dispose(false); } /// <summary> /// 非密封类修饰用protected virtual /// 密封类修饰用private /// </summary> /// <param name="disposing"></param> protected virtual void Dispose(bool disposing) { if (disposed) { return; } if (disposing) { // 清理托管资源 if (managedResource != null) { managedResource.Dispose(); managedResource = null; } } // 清理非托管资源 if (nativeResource != IntPtr.Zero) { Marshal.FreeHGlobal(nativeResource); nativeResource = IntPtr.Zero; } //让类型知道自己已经被释放 disposed = true; } public void SamplePublicMethod() { if (disposed) { throw new ObjectDisposedException("SampleClass", "SampleClass is disposed"); } //省略 } } class AnotherResource : IDisposable { public void Dispose() { } }
继承IDisposable接口也为实现语法糖using带来了便利。如:
using (SampleClass cl = new SampleClass()) { //省略 }
等价于:
SampleClass cl; try { cl == new SampleClass(); //省略 } finally { cl.Dispose(); }
如果存在两个类型一致的对象,using还可以这样使用:
using (SampleClass c1 = new SampleClass(),c2=new SampleClass()) { //省略 }
如果两个类型不一致,则可以这样使用:
using (SampleClass c1 = new SampleClass()) using (SampleAnotherClass c2 = new SampleAnotherClass()) { //省略 }
转自:《编写高质量代码改善C#程序的157个建议》陆敏技