Idisposable, Finalize和Destructor在C#中的关系
“有什么好的面试题没有?”这句话是最近同事们经常提到的话题。各位同事在每年的这个时候都成了标准的赶场面试官,今年他们最大的困惑就是感觉找新题难。找新题要有些原则,不能只靠智力题,也不能用那些BBS上随处可见的编程题。不过我想对于我的这些老大们应该还不是问题。
恰好,一位同事提到了Idisposable, Finalizer和Destructor在C#中的关系,我想咱还是从最基础的知识巩固一下吧,也算是给很长时间没更新的blog,恢复一下青春。
大家都知道在.NET中,Garbage collector(GC)能够帮助我们自动地清理分配在managed heap上的对象,这个的确有用。但是当我们在设计一个类的时候,如果使用到了非托管的资源,比如说Handle,DB connection等等,这个时候假如我们没有写出适当的逻辑对这些非托管资源进行释放,就会造成Handle Leak等资源泄露问题。那如何是好呢?有人说用“析构函数”怎么样?答案:不行。因为析构函数在C#中的行为是Non-deterministic的。其行为是由GC在某个时候调用的。
既然Destructor是C#用来执行清理操作的机制的话,它是在什么时候被调用呢?这下子Finalize的概念就要出场了。Finalize方法是定义在Object类中的,该方法在当对象不可访问后(没有指向该对象的引用)被自动调用,除非该对象已经使用GC.SuppressFinalize()方法从而免于执行其Finalize方法。这也是为了防止该对象调用了两次Finalize,否则会出现ObjectDisposedException异常哦。说道这里,真相揭晓:在C#中Finalize就是以Destructor的形式体现出来的。Finalize方法本身不能在C#中被调用或者重写。
说白了,Finalize方法是在用户忘记显示地释放系统资源的情况下,GC在调用Collector方法收回内存前,被CLR执行的一段“保险”代码。在这时,我们只需要清理那些非托管资源就好了,因为对象中的其他托管资源会在垃圾回收时被CLR处理。
最后,如果我们想更可控释放对象的资源该怎么办呢?对了,请事先接口IDisposable中的void Dispose()方法。它大多数情况下是用户在最后一次使用某对象后被调用。此时我们既要释放该对象所引用的其他对象的资源,也要同时释放其自身调用的非托管资源。如果该对象还是一个派生类的话,他还要负责释放基类的资源。最后别忘了在Dispose时调用GC.SuppressFinalize(this)方法,告诉CLR不要在执行Finalize方法了。
参考:
- Addison - C# Primer, A Practical Approach
- MSDN - ms-help://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.VisualStudio.v80.en/dv_fxadvance/html/eb4e1af0-3b48-4fbc-ad4e-fc2f64138bf9.htm (Very good code example)
浙公网安备 33010602011771号