我有以下简单的C#代码:
private Stack<Person> m_stack = new Stack<Person>();
public void Add<T>(T obj)
where T : Person
{
m_stack.Push(obj);
}
这将生成以下IL代码:
.method public hidebysig instance void
Add<(ConsoleApplication1.Person) T>(!!T obj) cil managed
{
// Code size 20 (0x14)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld class [System]System.Collections.Generic.Stack`1<class ConsoleApplication1.Person> ConsoleApplication1.Pool::m_stack
IL_0007: ldarg.1
IL_0008: box !!T
IL_000d: callvirt instance void class [System]System.Collections.Generic.Stack`1<class ConsoleApplication1.Person>::Push(!0)
IL_0012: nop
IL_0013: ret
} // end of method Pool::Add
所以我的问题是......为什么拳击? (IL_0008)我可以理解向下转换甚至编译错误,但为什么拳击(Person是引用类型......)
提前致谢!
答案 0 :(得分:31)
摘自Ecma-335 Partition III 4.1
如果 typeTok 是引用类型,则框指令不执行任何操作。
typeTok 在你的情况下是 !! T 。
我的猜测是,当编译器编译代码时,无论操作数的类型是否为引用类型,它总是调用 box 。由于 box 指令的语义,所以始终保证所需的结果。
答案 1 :(得分:3)
我相信这是通用方法约束对你做的 - 但是......
在任何情况下,根本不需要通用方法。只需将其写为:
public void Add(Person person)
{
m_stack.Push(person);
}
你会发现IL得到了简化,完全避免了这个问题。如果您要约束特定的引用类型,则可以使用该引用类型。
这更容易理解,更清晰。除非确实需要,否则我建议避免使用泛型方法调用。通用方法使类不那么明显,从长远来看,这意味着可读性和可维护性更低,并且更难以使用。