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