拳击和拆箱:什么时候出现?

所以我明白拳击和拆箱是什么。 什么时候出现在现实世界的代码中,或者在什么样的例子中出现问题? 我无法想象做这样的事情:

int i = 123; object o = i; // Boxing int j = (int)o; // Unboxing 

…但这几乎肯定是极度过于简单,我甚至可能在之前不知道它的情况下完成了装箱/拆箱。

现在它比仿制药之前的问题少得多。 现在,例如,我们可以使用:

 List x = new List(); x.Add(10); int y = x[0]; 

根本不需要拳击或拆箱。

以前,我们有:

 ArrayList x = new ArrayList(); x.Add(10); // Boxing int y = (int) x[0]; // Unboxing 

至少,这是最常见的拳击和拆箱经验。

如果没有generics参与,我想我可能会说反思是我参与的项目中拳击的最常见原因。 reflectionAPI总是使用“对象”来表示方法的返回值 – 因为它们没有其他方法可以知道要使用什么。

如果您不了解它,可能会引起您注意的另一个原因是您是否使用实现接口的值类型,并将该值传递给另一个以接口类型作为参数的方法。 同样,generics使这不是一个问题,但如果你不知道它,它可能是一个令人讨厌的惊喜。

拳击(根据我的经验)通常发生在这些情况下:

  • 值类型将传递给接受Object类型的参数的方法。
  • 值类型被添加到非generics集合(如ArrayList )。

其他时候你可以看到装箱和拆箱是你使用reflection,因为.NET框架的reflectionAPI大量使用Object

当值类型(如struct,int,long)传递到接受引用类型的某个位置(例如object )时,会发生装箱/取消装箱。

当您显式创建一个方法,该方法接受将传递值类型的object类型的参数时,会发生这种情况。 当您使用较旧的非generics集合来存储值类型(通常是基元)时,它也会出现。

当您使用String.Format()并将基元传递给它时,您还会看到发生拳击。 这是因为String.Format()接受一个params对象[] – 这导致在调用中装入其他参数。

使用reflection来调用方法也可能导致装箱/拆箱,因为reflectionAPI别无选择,只能返回object因为在编译时不知道实际类型(并且Reflection API不能是通用的)。

较新的通用集合不会导致装箱/拆箱,因此由于这个原因(例如ArrayList,Hashtable等)比旧集合更可取。 更不用说它们是类型安全的。

您可以通过更改接受通用对象的方法来避免装箱问题。 例如:

 public void string Decorate( object a ) // passing a value type results in boxing { return a.ToString() + " Some other value"; } 

VS:

 public void string Decorate( T a ) { return a.ToString() + " some other value"; } 

这是一个非常讨厌的:)

 SqlCommand cmd = ; // This code works very well. int result = (int)cmd.ExecuteScalar(); // This code will throw an exception. uint result = (uint)cmd.ExecuteScalar(); 

第二次执行失败,因为它尝试将Int32解包到UInt32中,这是不可能的。 所以你必须首先拆箱而不是投石。

 uint result = (uint)(int)cmd.ExecuteScalar(); 

拳击和拆箱实际上是从值类型转移到引用类型。 所以,把它想象成从堆栈转移到堆然后再回来。

肯定存在相关的情况。 在2.0框架中包含generics可以减少许多常见的拳击案例。

这种情况一直发生在人们不知道其含义是什么的时候,根本就不在乎,或者有时人们会忍不住接受拳击作为较小的等级。

当您访问值类型属性时,强类型数据行将一直打开/取消装箱。 此外,使用值类型作为接口引用也会将其打包。 或者从值类型的实例方法获取委托。 (委托的目标是Object类型)

自从使用generics与C#2.0(Visual Studio 2005)的强类型列表和词典出现以来,我认为保持装箱/拆箱的重要性已被惊人地最小化。 添加到那些可空类型( int?等)并使用空合并运算符( ?? ),它实际上不应该是一个很大的问题,并且可能不会在任何不是1.1 Framework或更早版本的代码中看到它。