本人最近接觸乙個專案,在這個專案裡面看到很多類實現了idisposable介面.在我以前的專案中都很少用過這個介面,只知道它是用來手動釋放資源的.這麼多地方用應該有它的好處,為此自己想對它有進一步的了解,但這個過程遠沒有我想象中的簡單.
idisposable介面定義:定義一種釋放分配的資源的方法。
.net 平台在記憶體管理方面提供了gc(garbage collection),負責自動釋放託管資源和記憶體**的工作,但它無法對非託管資源進行釋放,這時我們必須自己提供方法來釋放物件內分配的非託管資源,比如你在物件的實現**中使用了乙個com物件 最簡單的辦法可以通過實現finalize()來釋放非託管資源,因為gc在釋放物件時會檢查該物件是否實現了 finalize() 方法。 有一種更好的,那就是通過實現乙個介面顯式的提供給客戶呼叫端手工釋放物件的方法,而不是傻傻的等著gc來釋放我們的物件.這種實現並不一定要使用了非託管資源後才用,如果你設計的類會在執行時有非常大的例項(象 gis 中的geometry),為了優化程式效能,你也可以通過實現該介面讓客戶呼叫端在確認不需要這些物件時手工釋放它們 .
在定義乙個類時,可以使用兩種機制來自動釋放未託管的資源.這些機制通常放在一起實現.因為每個機制都為問題提供了略為不同的解決方法.這兩種機制是:
第一:宣告乙個析構函式,作為類的乙個成員.在gc**資源時會呼叫.
第二:在類中實現idisposable介面
析構函式的問題:
執行的不確定性:析構函式是由gc呼叫的,而gc的呼叫是不確定的.如果物件占用了比較重要的資源,應盡可以早的釋放資源.
idisposable介面定義了乙個模式,為釋未託管資源提供了確定的機制,並避免產生析構函式固有的與gc相關的問題.
在實際應用了,常常是結合兩種方法來取長補短.之所以要加上析構函式,是防止客戶端沒有呼叫dispose方法.
本人對idisposable介面的理解是這樣的:
這種手動釋放資源的方式肯定要比等待gc來**要效率高啊,於是出現了下面的示例類**:
這個foo類實現了idisposable介面,裡面有乙個簡單的方法:增加乙個使用者.
code
public class foo : idisposable
/**////
/// 虛方法,可供子類重寫
///
///
protected virtual void dispose(bool disposing)
// release unmanaged resources
m_disposed = true;}}
/**////
/// 析構函式
/// 當客戶端沒有顯示呼叫dispose()時由gc完成資源**功能
///
~foo()
/**////
/// 增加乙個使用者
///
public bool adduser()
/**////
/// 是否已經被釋放過,預設是false
///
public bool m_disposed;
//private intptr handle;
}客戶端是這樣呼叫的:先例項化物件,然後增加乙個使用者,此時銷毀物件.
code
foo _foo = null;
_foo = new foo();
//資源是否已經被釋放
//第一次預設為false;
bool isrelease3 = _foo.m_disposed;
//增加使用者
bool isadded= _foo.adduser();
//不再用了,釋放資源
_foo.dispose();
c#程式設計的乙個優點是程式設計師不需要擔心具體的記憶體管理,尤其是垃圾收集器會處理所有的記憶體清理工作。使用者可以得到像c++語言那樣的效率,而不需要考慮像在c++中那樣記憶體管理工作的複雜性。雖然不必手工管理記憶體,但如果要編寫高效的**,就仍需理解後台發生的事情。
一面執行沒有錯誤,可總想知道這個dispose方法到底做了些什麼.既然是釋放資源,那麼類被釋放後應該就被銷毀,它的引用應該是不存在的,於是本人的測試**如下:
code
tryelse
}catch (exception ex)
本想應該會執行response.write("資源已經釋放啦!"),可是結果相反,它的引用依然存在.這讓我不解,後來得到園友jyk的指點,他讓我試下,.net下實現了dispose方法的類,我就用stream試了下,測試結果好下:
code
stream _s = this.fileupload1.postedfile.inputstream;
//客戶端檔案大小 為了判斷物件是否被銷毀
long orglength = _s.length;
_s.dispose();
tryelse
}catch (exception ex)
執行結果我們可以非常清楚的看出,stream資源已經被釋放,因為兩次訪問stream的大小,發現在dispose後的大小為零.這就好像是第一次初始化的結果.但stream屬於非託管資源,如果是託管資源呢?在foo的測試**中發現,釋放前後的變數(m_disposed,呼叫dispose前為false,呼叫後為true,而且還可以呼叫類的方法)發生了變化,並不是我想象當中的初始化.這是讓我一直不解的地方.
後來在資料書上看,發現idisposable介面是專門針對未託管資源而設計的.它在託管資源上沒有特別大的幫助.最終的資源**工作還得要gc.我們看下託管資源和非託管資源在記憶體上的分配情況.
/*非託管資源一般都是放在堆疊上,而託管資源都是儲存在堆上.*/ 非常感謝 angel lucifer的指教,本人見笑了 特此刪除:
值型別與引用型別在記憶體分配上的分別:
值型別儲存在堆疊中,堆疊的工作原理就是先進後出.它在釋放資源的順序上與定義變數時分配記憶體的順序相反.值變數一旦出了作用域就會從堆疊中刪除物件.
引用型別則儲存在堆中.,當new乙個類時,此時就會為物件分配記憶體存入託管堆中,它可以在方法退出很長的時間後仍然可以使用.我以一句常用的例項類的語句來說明下.
classa a=new classa();
這句非常平常的語句其實可以分成兩部分來看:
第一:classa a;宣告乙個classa的引用a,在堆疊上給這個引用分配儲存空間.它只是個引用,並不是真正的物件.它包含儲存物件的位址.
第二:a=new classa();分配堆上的記憶體,以儲存真正的物件.然後修改a的值為新物件的記憶體位址.
當引用出了作用域後,就會從堆疊上刪除引用,但引用物件的資料仍然儲存在託管堆中,一直到程式停止,或者是gc刪除.
所在這點就可以解釋我上面寫的foo類在呼叫dispose方法後,程式仍然可以訪問物件的原因了.
/*我認為堆是否就有像asp.net中的快取功能,它可以將物件快取起來,物件只要建立一次就可以在一定的有限時間內存在.*/
非常感謝 angel lucifer的指教 特此更正如下:
這種情況完全是因為gc**操作的不可**性導致的。gc heap上的物件生存期完全看gc是否要**它而決定。此外,值型別完全沒必要實現 idisposable 介面。
總結:如果你的類中沒有用非託管資源,或者是非常大的例項(象 gis 中的geometry), 就沒有太大的必要實現這個介面. 並不是實現了這樣的介面就說明你寫的類有多大的不同或者會帶來多大的效能優勢.
實現IDisposable介面的方式和實際應用
實現idisposable介面的類才能在建立此類的碼中使用using來自動釋放資源,如 public class userbll idisposable region 成員方法 成員方法 endregion region 釋放類資源 private bool m disposed public vo...
IDisposable 介面 使用準則
準則一 只有在需要情況下才使用該介面,例如 1.你的類包含了非託管資源 2.你的類包含了 實現idisposable 介面 的屬性 準則二 對於有託管資源的類,繼承idisposable 介面,盡量不要 使用 析構函式,dispose方法越簡單越好。沒有必要對類裡面的託管資源 賦值 null.準則三...
對介面的理解
以下是我在ms news group裡,對 什麼情況下定義inte ce 這一問題的回答,基本上體現我現在對inte ce的理解,可能有不足或者完全就是謬誤,希望能得到大家的指正 乙個類實現了某個介面,就相當於宣告我能夠完成某項工作。比如乙個類實現了icomparable介面,就相當於宣告了我可以完...