奇怪的一般行为

时间:2011-07-13 12:33:36

标签: c# .net generics

在以下代码中,发生装箱(在Generic< Type> .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)。这是怎么回事?

3 个答案:

答案 0 :(得分:7)

不,它实际上不会包装盒子。来自ECMA-335对box指令的描述:

  

如果 typeTok 是引用类型,则box指令会将 val 更改为 obj

换句话说,如果您在引用类型上调用box,则{{1}}无害。

(JIT将为引用类型和值类型生成单独的本机代码,所以我怀疑这最终会在引用类型版本中被完全删除。)

答案 1 :(得分:1)

它为object选择Console.WriteLine重载,因为这是该调用最合适的重载。

请记住重载解析是在编译时完成的 - 编译器必须根据提供的类型信息选择合适的重载,在这种情况下,唯一合适的重载是object重载

要理解这一点,可能有助于忽略您的Main方法,并考虑Generic类在不同程序集中的情况。编译器需要选择一个重载,并且只知道Type可以被投射或装箱到object。仅仅因为 实际上代码中其他地方的代码使用带有string类型参数的类,这不会影响Generic编译的方式。

或者考虑如果Console.WriteLine没有接受object的重载会发生什么 - 在这种情况下该方法根本不会编译(因为Type没有限制会使另一个过载合适)。

答案 2 :(得分:0)

看起来像是重复使用的代码。

你可以试着强迫它不要这样做。

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