正確實現 IDisposable------
.NET中用於釋放物件資源的介面是IDisposable,但是這個介面的實現還是比較有講究的,此外還有Finalize和Close兩個函式。
public class Foo: IDisposable
{
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!m_disposed)
{
if (disposing)
{
// Release managed resources
}
// Release unmanaged resources
m_disposed = true;
}
}
~Foo()
{
Dispose(false);
}
private bool m_disposed;
}
在.NET的物件中實際上有兩個用於釋放資源的函式:Dispose和Finalize。Finalize的目的是用於釋放非託管的資源,而Dispose是用於釋放所有資源,包括託管的和非託管的。
在
這個模式中,void Dispose(bool
disposing)函式通過一個disposing引數來區別當前是否是被Dispose()呼叫。如果是被Dispose()呼叫,那麼需要同時釋放
託管和非託管的資源。如果是被~Foo()(也就是C#的Finalize())呼叫了,那麼只需要釋放非託管的資源即可。
這是因為,Dispose()函式是被其它程式碼顯式呼叫並要求釋放資源的,而Finalize是被GC呼叫的。在GC呼叫的時候Foo所引用的其它託管物件可能還不需要被銷燬,並且即使要銷燬,也會由GC來呼叫。因此在Finalize中只需要釋放非託管資源即可。
另外一方面,由於在Dispose()中已經釋放了託管和非託管的資源,因此在物件被GC回收時再次呼叫Finalize是沒有必要的,所以在Dispose()中呼叫GC.SuppressFinalize(this)避免重複呼叫Finalize。
-------這段話說得很明白
然而,即使重複呼叫Finalize和Dispose也是不存在問題的,因為有變數m_disposed的存在,資源只會被釋放一次,多餘的呼叫會被忽略過去。
因此,上面的模式保證了:
1、 Finalize只釋放非託管資源;
2、 Dispose釋放託管和非託管資源;
3、 重複呼叫Finalize和Dispose是沒有問題的;
4、 Finalize和Dispose共享相同的資源釋放策略,因此他們之間也是沒有衝突的。
在C#中,這個模式需要顯式地實現,其中C#的~Foo()函式代表了Finalize()。而在C++/CLI中,這個模式是自動實現的,C++的類解構函式則是不一樣的。
按
照C++語義,解構函式在超出作用域,或者delete的時候被呼叫。在Managed C++(即.NET
1.1中的託管C++)中,解構函式相當於CLR中的Finalize()方法,在垃圾收集的時候由GC呼叫,因此,呼叫的時機是不明確的。在.NET
2.0的C++/CLI中,解構函式的語義被修改為等價與Dispose()方法,這就隱含了兩件事情:
1、 所有的C++/CLI中的CLR類都實現了介面IDisposable,因此在C#中可以用using關鍵字來訪問這個類的例項。
2、 解構函式不再等價於Finalize()了。
對於第一點,這是一件好事,我認為在語義上Dispose()更加接近於C++解構函式。對於第二點,Microsoft進行了一次擴充套件,做法是引入了“!”函式,如下所示:
1 public ref class Foo
2 {
3 public:
4 Foo();
5 ~Foo(); // destructor
6 !Foo(); // finalizer
7 };
8
“!”函式(我實在不知道應該怎麼稱呼它)取代原來Managed C++中的Finalize()被GC呼叫。MSDN建議,為了減少程式碼的重複,可以寫這樣的程式碼:
1 ~Foo()
2 {
3 //釋放託管的資源
4 this->!Foo();
5 }
6
7 !Foo()
8 {
9 //釋放非託管的資源
10 }
11
對於上面這個類,實際上C++/CLI生成對應的C#程式碼是這樣的:
1 public class Foo
2 {
3 private void !Foo()
4 {
5 // 釋放非託管的資源
6 }
7
8 private void ~Foo()
9 {
10 // 釋放託管的資源
11 !Foo();
12 }
13
14 public Foo()
15 {
16 }
17
18 public void Dispose()
19 {
20 Dispose(true);
21 GC.SuppressFinalize(this);
22 }
23
24 protected virtual void Dispose(bool disposing)
25 {
26 if (disposing)
27 {
28 ~Foo();
29 }
30 else
31 {
32 try
33 {
34 !Foo();
35 }
36 finally
37 {
38 base.Finalize();
39 }
40 }
41 }
42
43 protected void Finalize()
44 {
45 Dispose(false);
46 }
47 }
48
由於~Foo()和!Foo()不會被重複呼叫(至少MS這樣認為),因此在這段程式碼中沒有和前面m_disposed相同的變數,但是基本的結構是一樣的。
並
且,可以看到實際上並不是~Foo()和!Foo()就是Dispose和Finalize,而是C++/CLI編譯器生成了兩個Dispose和
Finalize函式,並在合適的時候呼叫它們。C++/CLI其實已經做了很多工作,但是唯一的一個問題就是依賴於使用者在~Foo()中調
用!Foo()。
關於資源釋放,最後一點需要提的是Close函式。在語義上它和Dispose很類似,按照MSDN的說法,提供這個函式是為了讓使用者感覺舒服一點,因為對於某些物件,例如檔案,使用者更加習慣呼叫Close()。
相關文章
- 正確的equals實現
- 正確的折半查詢實現
- Redis分散式鎖的正確實現方式Redis分散式
- 實現Flutter彈窗的正確姿勢..Flutter
- 如何正確實現 Java 中的 HashCodeJava
- 分散式鎖實現的正確開啟方式分散式
- 如何正確實現Java中的hashCode方法Java
- 這才是實現分散式鎖的正確姿勢!分散式
- 「分散式」實現分散式鎖的正確姿勢?!分散式
- TensorFlow中RNN實現的正確開啟方式RNN
- [譯] 正確實現 linkedPurchaseToken 以避免重複訂閱
- 如何快速又正確地在C++裡實現鎖C++
- redis應用系列一:分散式鎖正確實現姿勢Redis分散式
- 如何實現 mysql 匯出資料,驗證頁面正確性?MySql
- 在iOS中如何正確的實現行間距與行高iOS
- 中國菜刀使用(實戰正確姿勢)
- Go 1.16 io/fs 設計與實現及正確使用姿勢Go
- Java排行榜中多級排序的一種正確實現方式Java排序
- 併發基礎-第01章-實現執行緒的正確方式執行緒
- Redis 分散式鎖的正確實現原理演化歷程與 Redission 實戰總結Redis分散式
- 層疊注意力模型:實現機器閱讀的正確姿勢模型
- redis應用系列三:延遲訊息佇列正確實現姿勢Redis佇列
- 層疊注意力模型 - 實現機器閱讀的正確姿勢模型
- 如何正確部署 QUICUI
- 正確高效使用 GoogleGo
- 正確使用rman crosscheckROS
- 正確理解ThreadLocalthread
- RoR的正確定位
- 以太坊實戰之《如何正確處理nonce》
- (翻譯)正確實施DevOps-The Lay of the Landdev
- 在現代引擎中使用正確的渲染打光流程
- 專案實施中如何正確理解“ERP”(轉)
- Spring系列之Mybatis動態代理實現全過程?回答正確率不到1%SpringMyBatis
- 如何正確且高效實現OSSIM中文化的解決方案(圖文詳解)
- 如何正確使用 Slim 框架框架
- Postman 正確使用姿勢Postman
- 如何正確處理nonce
- 正確理解CAP理論