所以我明白拳击和拆箱是什么。什么时候出现在现实世界的代码中,或者在什么样的例子中出现问题?我无法想象做这样的例子:
int i = 123;
object o = i; // Boxing
int j = (int)o; // Unboxing
......但这几乎肯定是极度过于简单,我甚至可能在以前不知情的情况下完成了装箱/拆箱。
答案 0 :(得分:32)
现在, 现在,例如,我们可以使用:
List<int> x = new List<int>();
x.Add(10);
int y = x[0];
根本不需要拳击或拆箱。
以前,我们已经:
ArrayList x = new ArrayList();
x.Add(10); // Boxing
int y = (int) x[0]; // Unboxing
这至少是我最常见的拳击和拆箱经验。
如果没有泛型参与,我想我可能会说反思是我参与过的项目中拳击的最常见原因。反射API总是使用“对象”来表示方法的返回值 - 因为他们没有其他方法可以知道要使用什么。
如果您不了解它,可能会引起您注意的另一个原因是您是否使用实现接口的值类型,并将该值传递给另一个以接口类型作为参数的方法。同样,泛型使这不是一个问题,但如果你不了解它,它可能是一个令人讨厌的惊喜。
答案 1 :(得分:8)
拳击(根据我的经验)通常发生在这些情况下:
Object
的参数的方法。ArrayList
)。其他时候你可以看到装箱和拆箱是因为.NET框架的反射API大量使用Object
时使用反射。
答案 2 :(得分:4)
当值类型(如struct,int,long)传递到接受引用类型的某处时(例如object
),就会发生装箱/取消装箱。
当您显式创建一个方法,该方法接受将传递值类型的object类型的参数时,会发生这种情况。当您使用较旧的非泛型集合来存储值类型(通常是基元)时,它也会出现。
当您使用String.Format()
并向其传递基元时,您还会看到拳击事件发生。这是因为String.Format()
接受了一个params对象[] - 这会导致在调用中装入其他参数。
使用反射调用方法也可能导致装箱/取消装箱,因为反射API别无选择,只能返回object
,因为在编译时不知道真实类型(并且反射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>( T a )
{
return a.ToString() + " some other value";
}
答案 3 :(得分:3)
这是一个非常讨厌的人:)
SqlCommand cmd = <a command that returns a scalar value stored as int>;
// 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();
答案 4 :(得分:1)
拳击和拆箱实际上是从值类型转移到引用类型。所以,把它想象成从堆栈转移到堆然后再回来。
确实存在相关的情况。在2.0框架中包含泛型可以减少许多常见的拳击案例。
答案 5 :(得分:1)
当人们不知道其含义是什么时,它总是发生,只是不在乎,或者有时人们会忍不住接受拳击作为较小的等级。
当您访问值类型属性时,强类型数据行将一直打开/取消装箱。 此外,使用值类型作为接口引用也会将其打包。或者从值类型的实例方法获取委托。 (委托的目标是Object类型)
答案 6 :(得分:0)
自从使用带有C#2.0的泛型(Visual Studio 2005)的强类型列表和词典出现以来,我认为保持装箱/拆箱的重要性已被惊人地最小化。添加到那些可空类型(int?
等)并使用空合并运算符(??
),它实际上应该不是很关注,并且很可能在任何代码中都看不到它这不是1.1框架或更早。