2011-04-05 18 views
9

我在教自己C#(我還不太瞭解)。在這個簡單的例子中:爲什麼n.GetHashCode()工作但n.GetType()拋出異常?

bool?   n = null; 

Console.WriteLine("n    = {0}", n); 
Console.WriteLine("n.ToString() = {0}", n.ToString()); 
Console.WriteLine("n.GetHashCode() = {0}", n.GetHashCode()); 

// this next statement causes a run time exception 

Console.WriteLine("n.GetType()  = {0}", n.GetType()); 

直覺上我明白爲什麼GetType()方法會拋出異常。實例n是空的,可以解釋這一點,但是,爲什麼在使用n.GetHashCode()和ToString()時爲什麼不會出於同樣的原因得到異常呢?

謝謝你的幫助,

約翰。

+5

檢查了這一點:http://stackoverflow.com/questions/194484/whats-the-strangest-corner-case-youve-seen-in-c-or-net/194671#194671 – mookid8000 2011-04-05 06:01:48

+0

mookid,偉大的線程。謝謝你指出。 – Hex440bx 2011-04-05 06:33:55

回答

14

GetHashCode()Nullable<T>覆蓋的虛方法:當它被稱爲一個Nullable<T>值時,Nullable<T>執行時,沒有任何拳擊。

GetType()不是虛方法,這意味着當它的名字,其值將首先盒裝......和拳擊「空」可爲空值的空引用結果 - 因此例外。我們可以從IL看到這一點:

static void Main() 
{ 
    bool? x = null; 
    Type t = x.GetType(); 
} 

被編譯爲:

.method private hidebysig static void Main() cil managed 
{ 
    .entrypoint 
    .maxstack 1 
    .locals init (
     [0] valuetype [mscorlib]System.Nullable`1<bool> nullable, 
     [1] class [mscorlib]System.Type 'type') 
    L_0000: nop 
    L_0001: ldloca.s nullable 
    L_0003: initobj [mscorlib]System.Nullable`1<bool> 
    L_0009: ldloc.0 
    L_000a: box [mscorlib]System.Nullable`1<bool> 
    L_000f: callvirt instance class [mscorlib]System.Type [mscorlib]System.Object::GetType() 
    L_0014: stloc.1 
    L_0015: ret 
} 

的重要位置位L_000a:在L_000f的callvirt指令之前,box指令。

現在比較,與等效代碼調用GetHashCode

static void Main() 
{ 
    bool? x = null; 
    int hash = x.GetHashCode(); 
} 

編譯爲:

.method private hidebysig static void Main() cil managed 
{ 
    .entrypoint 
    .maxstack 1 
    .locals init (
     [0] valuetype [mscorlib]System.Nullable`1<bool> nullable, 
     [1] int32 num) 
    L_0000: nop 
    L_0001: ldloca.s nullable 
    L_0003: initobj [mscorlib]System.Nullable`1<bool> 
    L_0009: ldloca.s nullable 
    L_000b: constrained [mscorlib]System.Nullable`1<bool> 
    L_0011: callvirt instance int32 [mscorlib]System.Object::GetHashCode() 
    L_0016: stloc.1 
    L_0017: ret 
} 

這一次,我們有callvirtconstrained指令/前綴,這基本上意味着「你不」當你調用虛擬方法時,不需要打開盒子。「從OpCodes.Constrained文檔:

受約束前綴被設計成允許在獨立的thisType是否是值類型或引用類型以統一的方式進行callvirt指令。

(按照鏈接以獲取更多信息。)

需要注意的是空值類型的裝箱的工作方式也意味着,即使是非空值,你不會得到Nullable<T>。例如,考慮:

int? x = 10; 
Type t = x.GetType(); 
Console.WriteLine(t == typeof(int?)); // Prints False 
Console.WriteLine(t == typeof(int)); // Prints True 

所以你走出類型是所涉及的非空的類型。撥打object.GetType()永不返回一個Nullable<T>類型。

+0

簡短而堅實的答案。 – 2011-04-05 06:06:46

+0

Jon!非常感謝你。我開始使用你的書「C#深度」來研究C#,但它很快就超出了我的頭腦。因此,我去了VS2008的「C#編程指南」。 你的解釋非常有意義。再次感謝你, 約翰 – Hex440bx 2011-04-05 06:08:29

+0

@Tomas:隨着時間的推移會變長,恐怕 - 但我喜歡詳細:) – 2011-04-05 06:08:32

相關問題