14

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

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

...但这几乎可以肯定是过于简单化了,我什至可能在以前不知道的情况下完成了装箱/拆箱。

4

8 回答 8

35

与泛型之前相比,现在的问题要少得多现在,例如,我们可以使用:

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 总是将“对象”用于方法的返回值之类的东西——因为它们没有其他方法知道要使用什么。

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

于 2009-12-22T21:05:34.423 回答
8

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

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

其他时候,您可以看到装箱和拆箱是在您使用反射时,因为 .NET 框架的反射 API 大量使用Object.

于 2009-12-22T21:03:24.813 回答
5

当值类型(如 struct、int、long)传递到某个接受引用类型(例如object.

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

String.Format()当您使用并将原语传递给它时,您还将看到装箱发生。这是因为String.Format()接受 params object[] - 这会导致调用中的附加参数装箱。

使用反射调用方法也可能导致装箱/拆箱,因为反射 API 没有选择,只能返回,object因为在编译时不知道真实类型(并且反射 API 不能是通用的)。

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

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

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

与:

public void string Decorate<T>( T a )
{
   return a.ToString() + " some other value";
}
于 2009-12-22T21:06:41.083 回答
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();
于 2009-12-22T22:05:07.153 回答
1

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

当然,在某些情况下,这是相关的。在 2.0 框架中包含泛型将许多常见的装箱案例排除在实践之外。

于 2009-12-22T21:05:37.763 回答
1

当人们不知道其中的含义是什么,根本不在乎,或者有时人们不得不接受拳击作为次要水平时,这种情况总是会发生。

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

于 2009-12-22T21:09:35.707 回答
-1

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

于 2009-12-22T21:06:37.807 回答
-2

“ArrayList 的类型参数必须是类,而不是原始类型,因此 Java 为原始类型提供了包装类,例如 int 的“Integer”、double 的“Double”等。

更多解释:

数组是元素的编号序列,每个元素就像一个单独的变量。”

Java 为数组元素(以及 Java 中的其他集合类型)的“for”循环提供了一种特殊的语法。简化的语法称为“for-each”循环。例如,以下语句将每个字符串打印在一个名为“words”的数组中。

for (String word : words) System.out.println(word);

数组有许多元素,这些元素在创建数组对象时设置并且不能更改。Java 为动态数组的功能提供了“ArrayList”类,该数组的大小可以改变。ArrayList 是参数化类型的一个示例,一种依赖于另一种类型的类型。” Eck (2019)

参考资料 : Introduction to Programming Using Java, Eck (2019) 在第 7 章中描述如下

于 2021-10-19T22:43:11.607 回答