在C#中使用泛型时的装箱

时间:2009-03-14 19:09:51

标签: c# generics

我有以下简单的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是引用类型......)

提前致谢!

2 个答案:

答案 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得到了简化,完全避免了这个问题。如果您要约束特定的引用类型,则可以使用该引用类型。

这更容易理解,更清晰。除非确实需要,否则我建议避免使用泛型方法调用。通用方法使类不那么明显,从长远来看,这意味着可读性和可维护性更低,并且更难以使用。