奇怪的通用行为
在下面的代码,拳击发生(在通用<类型> .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)。这里发生了什么?
及其选择的object
过载Console.WriteLine
因为这是该呼叫的最合适的超载。
记住重载是在编译时做 - 编译器必须选择基于所提供的信息类型合适的过载,而在这种情况下,只适合一个是object
超载。
要理解这一点,可能会帮助忽略您的Main
方法,并考虑Generic
类处于不同装配中的情况。编译器需要选择一个过载,并且只知道Type
可以投射或装入object
。仅仅因为是事实上,程序集中的其他代码中使用此类的代码与string
类型参数不会影响编译Generic
的方式。
或者考虑是否Console.WriteLine
没有过载受理object
会发生什么 - 在这种情况下,方法根本就无法编译(因为有上Type
没有限制这将使另一个重载合适)。
谢谢。你的答案解决了我的问题的一部分,Jon Skeet的答案解决了另一部分问题,但我只能选择一个接受的答案。既然你的回答解决了我更关心的部分,我选择了你的。但是,所有答案都是有用的。 – Tom
看起来像它的重用代码。
你可以试着强迫它不这样做。
public void Print<Type>()
{
Console.WriteLine(value);
}
不,实际上它不会装箱。从box
指令的ECMA-335描述:
如果typeTok是引用类型,盒指令不返回VAL不变OBJ。
换句话说,如果您在引用类型上调用box
,则box
是无害的。
(该JIT将生成用于引用类型和值的类型单独的本机代码,无论如何,所以我怀疑这只是最终的引用类型版本被完全除去。)
+1是的,我在4.1节看到。 '确实返回',他们需要一些证明读者:P – SwDevMan81
看起来像使用一些代码重用。 –
Fyi,调用泛型参数'Type'是不好的做法,因为它可能与'System.Type'冲突。 –