本文翻译自IDisposable:What Your Mother Never Told You About Resource Deallocation
译者:爱让一切都对了
(有删简)
IDisposable
这篇文章的第一部分讨论了IDisposable
接口的问题,这部分将着眼于实现IDisposable的“最佳做法”。
IDisposable
的问题:用可销毁设计模式,少用IDisposable微软推荐的IDisposable
代码模式很复杂,因为他们试图用这一个模式实现所有可能的情况。
对于每一个非托管资源,创建一个(可能是internal
)IDisposable
包装类,负责释放这个非托管资源。微软在BCL(BasicClass Library)中遵循了这一原则。注意,包装了非托管资源的IDisposable类,应当认为是一种托管资源。
不要从非托管资源的包装类继承。
一个类拥有托管资源,或派生自拥有托管资源的类时,要实现IDisposable
。
不要在一个类中同时考虑托管和非托管资源。
根据以上指导原则,可销毁设计原理应当是这样的:
0级类型直接包装非托管资源。这些类型通常是密封的。
1级类型是派生字1级类型,或包含有0级或1级类型的字段。
包装非托管资源的类,越靠近原生API越好。这些包装类应该很小,而且是私有的或内部的。包装类的唯一任务就是负责销毁非托管资源。其他功能要在1级类中实现。1级类的字段可能有0级类。总的来说,
0级类型只处理非托管资源;1级类型只处理(父类中的或字段中的)托管资源。
在1级类型上实现IDisposable
很简单:在IDisposable.Dispose
里调用字段的Dispose方法,然后调用base.Dispose()。
Dispose
可以多次调用。
1级类型没有终结器。
没必要调用GC.KeepAlive(this)
。
调用GC.SuppressFinalize(this)
也是不必要的。
话说回来,为0级类型实现IDisposable
确实很困难。能不碰它就不要碰它。
IDisposable
的问题:用辅助类虽说不要碰0级类型,但确实常常要写的非托管资源包装类,比如说一些数据结构的指针。微软的System.Runtime.InteropServices.SafeHandle
、System.Runtime.InteropServices.CriticalHandle
、Microsoft.Win32.SafeHandles
会让你写起来简单一点。
0级类型应该始终继承自SafeHandle
。CriticalHandle
,从名字上看起来就不那么安全,最好不要使用。
所以说写一个新的非托管资源的类包装有四种情况(从易到难):
已经有一个0级的非托管资源的类型,SafeHandle
的子类。这样的话,程序员仅需要创建一个新的1级类型。
非托管资源是一个指针类型,但微软没帮我写好一个0级类型。这样的话,程序员需要创建两个类,一个0级类型和一个1级类型。
要包装不仅有指针,还有其他一些数据。这样的话,程序员也是一个0级类型和一个1级类型。当然那个0级类型会比较复杂。
非托管资源根本就不是指针。这样的话,程序员也是一个0级类型和一个1级类型。当然那个0级类型会比较复杂。
注意,从1级类型继承时,通常会是声明一个受保护的1级类型的属性,与其相关的字段一般0级类型有些关系。