12

可能的重复:
为什么我们需要在 C# 中装箱和拆箱?
什么是装箱和拆箱,权衡取舍是什么?

在 C# 中,sit 是什么意思:“Box and UnBox”?

这是我创建 Text 的 MSDN 的摘录。

但这种便利是有代价的。添加到 ArrayList 的任何引用或值类型都会隐式向上转换为 Object。如果项目是值类型,则在将它们添加到列表时必须将它们装箱,并在检索时取消装箱。铸造以及装箱和拆箱操作都会降低性能;在您必须迭代大型集合的情况下,装箱和拆箱的效果可能非常重要。

谢谢!

4

3 回答 3

28

这是一个更详细的解释,着眼于公共语言运行时的内部。

首先,让我们区分值类型引用类型

  • 值类型保存在堆栈中,并将其副本传递给调用的方法
  • 引用值保存在托管堆中,堆栈只保存指向其位置的指针(引用)。位置,而不是对象,被传递给被调用的方法

如果您不知道堆栈是什么(不要被冒犯),它是一个内存区域,用于保存方法中的局部变量和用于return指令的调用者函数的地址(只是为了简短并提供一般性答案)。当你调用一个方法时,堆栈上足够的区域被静态分配给它,所以堆栈分配总是被称为静态分配。

相反,堆是与堆栈分离的内存区域,是运行进程的属性,必须首先要求操作系统在其中分配,这就是为什么它被称为动态分配(如果你不在 if 语句中运行,例如,可能不会为您的进程分配内存,而是始终分配堆栈)。

举个关于堆和栈的最后一个例子:在 C++ 等语言中,int[100] a;静态声明会在堆栈上分配 100*8 字节(假定为 64 位系统),而在堆栈上int* a = new int[100];声明一个 8 字节(在 64 位系统上)区域堆栈 AND 请求堆上的 800 个字节(如果可用)。

现在让我们谈谈C#:

拳击

由于 int 是一种值类型,并且是在堆栈上分配的,因此当您将其强制转换为 object 或任何其他引用类型时(实际上没有其他引用类型可以从 int 继承,但这是一般规则),该值必须成为必然一个引用类型。因此,堆上的一个新区域被分配,对象被装箱在其中,堆栈保存一个指向它的指针。

拆箱

恰恰相反:当你有一个引用类型,比如 object,并且想要将它转换为值类型,比如 int,新值必须保存在堆栈中,所以 CLR 进入堆,取消装箱值并将其复制到堆栈中。

换句话说

还记得int[]int*例子吗?简单地说,当您int在 C# 中拥有时,运行时希望其堆栈位置保存该值,但当您拥有时object,它希望其实际值位于堆栈指向的堆位置。

于 2011-01-25T14:50:38.810 回答
1

.net Framework 中有两种不同的类型。

值类型,例如 int、double、single

ReferenceTypes ArrayList List 等等

ValueTypes类型的变量存储在 Stack ReferenceTyped变量存储在堆中

ValueTypes类型的变量存储 VALUE ReferenceTyped变量将 REFERENCE 存储到一个值

因此,如果您复制ValueType变量 - 存在值的真实副本,但如果您复制ReferenceType变量 - 您将获得对 SAME 变量的额外引用。

在您的问题中装箱意味着, valueType 变量(例如 int)将像引用类型变量一样被处理 - .net 将其放入一个新框中。所以它将被封装在堆中,并且会有对它的引用。

如果您希望该值再次出现在 valueType 变量中,则必须将其拆箱(将其从框中取出)。因此,该值将从堆中取出 - 并再次存储/提供给堆栈。

于 2011-01-25T14:49:52.657 回答
0

ArrayList 只存储对象。对于引用类型(如String),这没有问题,但对于 ValueType (int, DateTime, ..) 是。

这些值类型需要先转换为对象,然后才能将它们存储为普通对象。这种“转换为对象”称为“装箱”,需要一点时间。

当您读回该值时,您需要从 Object 转换为int(或任何它)。这称为“拆箱”。

于 2011-01-25T14:42:51.197 回答