2011-07-13 66 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