C#中IDispose接口的实现及为何这么实现详解

2019-12-30 19:45:14王振洲

例如:


public void Cleanup() 
public void Shutdown() 
…… 

你可以这么做,但是有一个标准的名字


public void Dispose() 

甚至有一个接口IDisposeable,里面包含的就是刚才那个方法


public interface IDisposable 
{ 
 void Dispose() 
} 

因此最好的办法是让你的类去实现IDisposable接口,在接口内的Dispose方法内提供一段清除非托管资源的代码。


public void Dispose() 
{ 
 //这里释放一个句柄(句柄是一个非托管资源,属于Win32编程的概念) 
 Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); 
} 

OK。这就完成了,除非你想做的更好!

2.3、别忘了类中的托管资源还占着空间!

托管资源占着空间?你首先想到的可能是那些int,string等等这些托管资源,它们能占用几个空间,他们占着就占着呗!

但是托管资源可不仅仅是那些资源,要是你的对象使用了250MB的System.Drawing,Bitmap(这是在.Net Frame中的,属于托管资源)作为一些缓冲怎么办?当然,你知道这是一个.Net的托管资源,所以GC理所应当的将会释放它。但是你真的想留着250MB的内存空间就那么被占用着?然后等待着GC最终释放它?更或者要是有一个更大数据库连接呢?我们当然不想让那连接白白占用来等待GC的终结!

如果用户调用了Dispose方法(意味着他们不再想使用这个对象里的一切)为什么不去扔掉那些浪费空间的位图资源和数据库连接呢?

那么,我们就应该这么做:

释放非托管资源(因为我们必须这么做) 释放托管资源(让你的Dispose更完美)

所以,让我们更新我们的Dispose方法来释放那些托管资源


public void Dispose() 
{ 
 //Free unmanaged resources 
 Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); 
 
 //Free managed resources too 
 if (this.databaseConnection !=null) 
 { 
 this.databaseConnection.Dispose(); 
 this.databaseConnection =null; 
 } 
 if (this.frameBufferImage !=null) 
 { 
 this.frameBufferImage.Dispose(); 
 this.frameBufferImage = null; 
 } 
} 

OK,做的很好了,除非你想做的更好!

2.4、总会有人粗心忘记调用Dispose!

要是有人使用你的类创建了对象,但是忘记调用Dispose方法该怎么办?这将会泄露一些非托管的资源!

注意:忘记调用Dispose方法虽然会造成非托管资源的泄露,但是对于那些托管资源来说,是不会泄露的,因为最终GC会行动起来,在后台线程中释放那些和托管资源有关的内存空间。这包括你创建的对象和其中的托管资源(例如Bitmap和数据库连接) (为什么?往下看)

也就是说,如果你忘记调用Dispose方法,这个类应该自动进行一些补救措施!我们可以想到设计一种方法来做为一种后备方法:利用GC最终调用的终结器