2011-07-13 96 views
3

在下面的代碼,拳擊發生(在通用<類型> .PRINT):奇怪的通用行爲

using System; 

namespace Test 
{ 
    static class Program 
    { 
     static void Main() 
     { 
      Generic<string> generic = new Generic<string>("test"); 
      generic.Print(); 
     } 
    } 

    class Generic<Type> 
    { 
     Type value; 

     public Generic(Type value) 
     { 
      this.value = value; 
     } 

     public void Print() 
     { 
      Console.WriteLine(value); 
     } 
    } 
} 

ILSpy輸出:

.method public hidebysig 
    instance void Print() cil managed 
{ 
    // Method begins at RVA 0x207d 
    // Code size 17 (0x11) 
    .maxstack 8 

    IL_0000: ldarg.0 
    IL_0001: ldfld !0 class Test.Generic`1<!Type>::'value' 
    IL_0006: box !Type 
    IL_000b: call void [mscorlib]System.Console::WriteLine(object) 
    IL_0010: ret 
} // end of method Generic`1::Print 

這是拳擊和調用Console.WriteLine(對象) 。我認爲它只會調用Console.WriteLine(string)。這裏發生了什麼?

+0

看起來像使用一些代碼重用。 –

+3

Fyi,調用泛型參數'Type'是不好的做法,因爲它可能與'System.Type'衝突。 –

回答

1

及其選擇的object過載Console.WriteLine因爲這是該呼叫的最合適的超載。

記住重載是在編譯時做 - 編譯器必須選擇基於所提供的信息類型合適的過載,而在這種情況下,只適合一個是object超載。

要理解這一點,可能會幫助忽略您的Main方法,並考慮Generic類處於不同裝配中的情況。編譯器需要選擇一個過載,並且只知道Type可以投射或裝入object。僅僅因爲事實上,程序集中的其他代碼中使用此類的代碼與string類型參數不會影響編譯Generic的方式。

或者考慮是否Console.WriteLine沒有過載受理object會發生什麼 - 在這種情況下,方法根本就無法編譯(因爲有上Type沒有限制這將使另一個重載合適)。

+0

謝謝。你的答案解決了我的問題的一部分,Jon Skeet的答案解決了另一部分問題,但我只能選擇一個接受的答案。既然你的回答解決了我更關心的部分,我選擇了你的。但是,所有答案都是有用的。 – Tom

0

看起來像它的重用代碼。

你可以試着強迫它不這樣做。

public void Print<Type>() 
{ 
    Console.WriteLine(value); 
} 
7

不,實際上它不會裝箱。從box指令的ECMA-335描述:

如果typeTok是引用類型,盒指令不返回VAL不變OBJ

換句話說,如果您在引用類型上調用box,則box是無害的。

(該JIT將生成用於引用類型和值的類型單獨的本機代碼,無論如何,所以我懷疑這只是最終的引用類型版本被完全除去。)

+0

+1是的,我在4.1節看到。 '確實返回',他們需要一些證明讀者:P – SwDevMan81