1. Object類再分析:
System.Object是所有.Net類的基類,包括值類型和引用類型。值類型為什麼也是繼承於System.Object的呢?Object不是引用類型嗎?這個就涉及了.Net的一個有趣而神奇的機制--裝箱和拆箱(box&un-box)。這個後面會提到。
Object是所有類(class)和結構(struct)的基類。Class都是繼承於Object類的,struct都是繼承於System.ValueType的,而System.ValueType是繼承Object。
好了,既然Object類級別這麼高,那麼它應該包含哪些成員呢?當然是不會有成員變量了,這東西不可以在那麼高級的類裡存在的,而且也沒有必要。不過Object類定義了很多通用的方法,如果你新建的類沒有重載這些方法,那你的新建類就會自動繼承Object類的這些方法。我把這些通用方法列於下表,以便大家參考:
方法名 前綴修飾符 作用
string ToString() public virtual 返回對象的字符串表示
int GetHashTable() public virtual 在實現字典(散列表)時使用 這是在集合(散列表中)使用的對象
bool Equals(object obj) public virtual 對對象的實例進行相等比較
bool Equals(object obj1, object obj2) public static 對對象的實例進行相等比較
bool ReferenceEquals(object obj1, object obj2) public static 比較兩個引用是否指向同一個對象
Type GetType() public 返回對象類型的詳細信息
object MemberwiseClone() protected 進行對象的淺表復制
void Finalize() protected virtual 析構函數
在上面我們看到了一些像virtual,static,protected這樣的修飾符,我在學習C#語言機制的時候曾經對這些內容做了些詳細的整理,請參考《C#修飾符大總結》。
我們看virtual修飾符,這說明某些方法是能夠被重載的。比如說ToString()這樣的方法。我們自己所定義的類不特別聲明的話,都會繼承Object。也就是說Object的這些公用方法存在於每一個類中,無論是.Net Framework中的類還是自定義的類。
2. 垃圾回收機制,dispose() & Finalize()
在《C#的數據類型以及內存管理機制剖析(1)》中我提到了.Net的垃圾回收機制。這裡在簡要地提一下,這個機制真的是.Net核心內容之一,盡管不能夠被直接使用,但理解其機制卻相當重要。能夠在程序中避免很多不必要的錯誤。GC的流程如下圖所示:
我們可以在程序中調用垃圾回收:
view sourceprint?1 System.GC.Collect()
不過請大家小心,並不是調用了這段代碼後,垃圾回收就能夠回收所有未引用對象。這個也是要分情況的,有時候釋放了集合對象,但是集合中的元素並不一定會被釋放。這還要看這個元素對象是否也出了代碼作用域(代碼中表示就是,離開包含該元素的某一層括號)。垃圾回收機制能夠很好得管理托管資源和內存,但是在處理一些非托管資源比如文件句柄、網絡連接、數據庫、特殊設備等,就會有一些問題。
(1).Net析構函數Finalize()
view sourceprint?1 class TestClass
2 {
3 ~TestClass()
4 {
5 //implementation
6 }
7 }
事實上這個析構函數會在生成IL時被編譯成下面的形式,會自動得生成Finalize方法:
protescted overide void Finalize()
{
try
{
//析構函數執行
}
finally
{
base.Finalize();
}
}
析構函數會在GC釋放對象之前調用,由於.Net垃圾回收的機制,我們無法控制析構函數的執行的時間,也無法預知析構函數。所以如果我們需要去釋放非托管資源,把這個釋放的操作放在析構函數Finalize()中會遇到麻煩。主要有幾個原因:①我們無法控制析構函數在特定的時候來執行,這樣有些重要資源使用完就應該立刻釋放的,就不適合放置這裡。②也不可能控制Finalize的執行順序,但某些對象有關聯,需要順序釋放的情況也是不能完成的。③析構函數執行,會延遲該對象內存釋放的時間。必須要等到第二次GC才有可能完全釋放改對象。④運行庫用一個線程順序鏈式得執行每個需要執行的Finalize方法,這個隊列會影響性能。
綜上所述,Finalize()是不適合在.Net程序中對一個對象的資源釋放和最後處理,同時對執行時間有要求的一些操作。C#推薦使用System.IDisposable來取代這個析構函數。加入Finalize後的GC機制如圖所示:
(2)IDisposable接口
IDisposable提供了可以在確定的時機釋放未托管資源的功能。其避免了析構函數以為垃圾收集機制而產生的一些問題。在對象結束的時候顯式得調用Dispose()方法可以直接做一些釋放資源的操作。
view sourceprint?1 Class TestClass:IDisposable
2 {
3 public void Dispose()
4 {
5 //implementation
6 }
7 }
上面是實現IDisposable這個接口,我們為了使一個對象總是能執行Dispose方法,通常我們會把Dispose()放在try/catch的Final段,也就是無論怎樣都一定會執行,包括異常情況。假設我們有一個CriticalResource類,那麼可以這麼做:
CriticalResource Instance = null;
try
{
Instance = new CriticalResource;
//processing code
}
finally
{
if(Instance != null)
Instance.Dispose();
}
//也可以用下面的方式,使用using塊,可以減少代碼量,起到同樣效果
using(CriticalResource Instance = new CriticalResource)
{
//processing
}
(3)Finalize和Dispose的雙重實現
可以將Dispose()作為結束對象的方法,Finalize做為安全機制(沒有調用Dispose是作為保險的方式)。這個方式應該是最佳釋放資源解決方案,不過比較復雜些,可以參考下面的簡單實現。
view sourceprint?01 public class CResource : IDisposable
02 {
03 //記錄是否已經執行Dispose
04 Private bool isDispose = false;
05 //實現Dispose方法
06 Public void Dispose()
07 {
08 //釋放資源
09 Dispose(true);
10 //GC禁止使用finalize析構函數
11 GC.SuppressDinalize(this);
12 }
13
14 protected virtual void Dispose(bool disp)
15 {
16 //沒有執行過Dispose
17 if(!isDisposed)
18 {
19 if(disp)
20 {
21 //釋放托管資源
22 }
23 //釋放非托管資源
24 }
25 isDisposed = true;
26 }
27
28 ~CResource()
29 {
30 Dispose(false)
31 }
32
33 public TestMethod()
34 {
35 //確保執行時資源未釋放
36 if(isDisposed)
37 {
38 throw new Exception();
39 }
40 }
41
42 }