在C#裡面有2種機制來釋放未託管資源:
- 聲明一個解構函式(或終結器),作為類的一個成員
- 在類中執行System.IDisposable介面
解構函式
下面這段代碼是一段帶有解構函式的簡單代碼:
using System;using System.Collections.Generic;using System.Linq;using System.Text; namespace MemRelease{ class Program { ~Program() { // Orders. } static void Main(string[] args) { } }}
在IL DASM中,你會發現並沒有這個析構的方法。C#編譯器在編譯解構函式時,會隱式地把解構函式的代碼編譯為Finalize()方法的對應代碼,確保執行父類的Finalize()方法 看下這段代碼中對於解構函式的編譯:
.method family hidebysig virtual instance void Finalize() cil managed{ // Code size 14 (0xe) .maxstack 1 .try { IL_0000: nop IL_0001: nop IL_0002: leave.s IL_000c } // end .try finally { IL_0004: ldarg.0 IL_0005: call instance void [mscorlib]System.Object::Finalize() IL_000a: nop IL_000b: endfinally } // end handler IL_000c: nop IL_000d: ret} // end of method Program::Finalize
是一個try…finally的結構,
try{// destructor implementation}finally{base.Finalize();}
~Program()解構函式中執行的代碼封裝在Finalize()方法的一個try塊中。對父類Finalize()方法的調用放在finally塊中,確保該調用的執行。
使用解構函式來釋放資源有幾個問題:
- 與C++解構函式相比,C#解構函式的問題是他們的不確定性。在刪除C++對象時,其解構函式會立即執行,但是由於垃圾收集器的工作方式,無法確定C#對象的解構函式何時執行。
- C#解構函式的執行會延遲物件最終從記憶體中刪除的時間。有解構函式的對象需要2次處理才能刪除:第一次調用解構函式時,沒有刪除對象,第二次調用才真正刪除對象。
IDisposable介面
在C#中,推薦使用System.IDisposable介面替代解構函式。IDisposable介面定義一個模式,為釋放未託管的資源提供了確定的機制,並避免產生解構函式固有的與垃圾函數器相關的問題。
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace MemRelease{ class Program : IDisposable { public void Dispose() { // implementation } static void Main(string[] args) { } }}
假定有一個類ResourceGobbler,它使用某些外部資源,且執行IDisposable介面。如果要執行個體化著各類的執行個體,使用它,然後釋放它,就可以使用下面的代碼。
ResourceGobbler theInstance = new ResoucrGobbler();// do your processingtheInstance.Dispose();
如果加入異常處理:
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace MemRelease{ public class ResourceGobbler : IDisposable { public void Dispose() { //implementation } } class Program { static void Main(string[] args) { ResourceGobbler theInstance = null; try { theInstance = new ResourceGobbler(); // do your processing } finally { if (theInstance != null) { theInstance.Dispose(); } } } }}
即使在處理過程中出現異常,這個版本也可以確保總是在theInstance上調用Dispose(),總能釋放有theInstance使用的資源。
C#提供了一種文法,可以確保執行IDisposal介面的對象的引用超出範圍時,在該對象上自動調用Dispose().
using (ResourceGobbler theInstance = new ResourceGobbler());{ // do your processing}