C#中資源分為托管資源和非托管資源。 托管資源由垃圾回收器控制如何釋放,不需要程序員過多的考慮(當然也程序員也可以自己釋放)。 非托管資源需要自己編寫代碼來釋放。那么編寫好的釋放非托管資源的代碼(釋非代碼)由誰來調用呢。有兩種實現方式:
一 將釋非代碼放到構造函數析構函數中,由系統自動調用,系統會在資源對象不再使用了,會在某個時間調用構造函數析構函數來釋放非托管資源。構造函數析構函數的目的就是用來釋放或清理非托管資源的。但它有一個問題是調用的時間是系統說了算,不能在程序中自己想要調用時調用析構函數,這是C#規定的。那么就產生了第二種方式。
二 將釋非代碼放到另外一個函數中,當自己想調用時就調用。將釋非代碼放在一個方法中共用,代碼如下:
1 MyClass 2 { 3 ~MyClass() 4 { 5 DisposePro(); 6 } 7 8 public void Dispose() 9 { 10 DisposePro(); 11 } 12 13 private void DisposePro() 14 { 15 // 釋非代碼 16 ...... 17 } 18 }
但是這樣可能會產生其他問題。資源可能會被多次釋放,而產生問題。系統會自動調用析構函數,自己也可能多次調用Dispose()方法。那么解決方法是使用一個全局變量作為標記,來標記資源是否已經被釋放,已經釋放就不再釋放。代碼如下:
1 MyClass 2 { 3 private bool disposed = false; 4 ~MyClass() 5 { 6 DisposePro(); 7 } 8 9 public void Dispose() 10 { 11 DisposePro(); 12 } 13 14 private void DisposePro() 15 { 16 if(disposed == false) 17 { 18 // 釋非代碼 19 ...... 20 } 21 disposed = true; 22 } 23 }
這樣看起來似乎沒有問題了。但是當調用Dispose()方法只能立即釋放非托管資源,而托管資源還是需要由GC自動處理。那么為了能夠做到調用Dispose()方法時也能夠釋放立即釋放托管資源,則需要在DisposePro()方法中添加上想要釋放的托管資源的釋放代碼(釋放托管代碼)。代碼如下:
1 MyClass 2 { 3 private bool disposed = false; 4 ~MyClass() 5 { 6 DisposePro(); 7 } 8 9 public void Dispose() 10 { 11 DisposePro(); 12 } 13 14 private void DisposePro() 15 { 16 if(disposed == false) 17 { 18 // 釋托管代碼 19 ...... 20 // 釋非代碼 21 ...... 22 } 23 disposed = true; 24 } 25 }
這樣還是有問題,當析構函數調用DisposePro()時,會調用釋托管代碼,可能產生問題——托管對象可能已經被GC刪除了而產生問題。那么使用一個標記給DisposePro(),當是被析構函數調用時不執行釋托管代碼。重新命名DisposePro(),代碼如下:
1 MyClass 2 { 3 private bool disposed = false; 4 ~MyClass() 5 { 6 Dispose(false); 7 } 8 9 public void Dispose() 10 { 11 Dispose(true); 12 } 13 14 private void Dispose(bool disposing) 15 { 16 if(disposed == false) 17 { 18 if(disposing == true) 19 { 20 // 釋托管代碼 21 ...... 22 } 23 // 釋非代碼 24 ...... 25 } 26 disposed = true; 27 } 28 }
用這段代碼來釋放資源應該沒有問題了。看一下標准清理模式,代碼如下:
1 MyClass:IDisposable 2 { 3 private bool disposed = false; 4 ~MyClass() 5 { 6 Dispose(false); 7 } 8 9 public void Dispose() 10 { 11 Dispose(true); 12 GC.SuppressFinalize(this); 13 } 14 15 private void Dispose(bool disposing) 16 { 17 if(disposed == false) 18 { 19 if(disposing == true) 20 { 21 // 釋托管代碼 22 ...... 23 } 24 // 釋非代碼 25 ...... 26 } 27 disposed = true; 28 } 29 }
標准清理模式中多了一句GC.SuppressFinalize(this);【該方法通知CLR不要調用該方法的析構函數,因為它已經被清理了。】如果沒有這句代碼,我認為不影響程序的正確性,不會發生安全問題,他只是告訴系統不要再調用構造函數了。那么為什么要加上這句代碼呢?如果在調用了Dispose()之后再調用析構函數只是多此一舉,所以告訴系統不要再調用了。這一點應該和性能有關系。【如果不需要構造函數就不要執行構造函數,他們會帶來性能上的開銷】。
參考:
C#4.0 圖解教程 Daniel M. Solis
相關鏈接:
1. 實現 Finalize 和 Dispose 以清理非托管資源 http://msdn.microsoft.com/zh-cn/library/b1yfkh5e(v=vs.90).aspx
2. GC.SuppressFinalize 方法 http://msdn.microsoft.com/zh-cn/library/system.gc.suppressfinalize(v=vs.90).aspx