在 C# 中,sit 是什么意思:“Box and UnBox”?
这是我创建 Text 的 MSDN 的摘录。
但这种便利是有代价的。添加到 ArrayList 的任何引用或值类型都会隐式向上转换为 Object。如果项目是值类型,则在将它们添加到列表时必须将它们装箱,并在检索时取消装箱。铸造以及装箱和拆箱操作都会降低性能;在您必须迭代大型集合的情况下,装箱和拆箱的效果可能非常重要。
谢谢!
在 C# 中,sit 是什么意思:“Box and UnBox”?
这是我创建 Text 的 MSDN 的摘录。
但这种便利是有代价的。添加到 ArrayList 的任何引用或值类型都会隐式向上转换为 Object。如果项目是值类型,则在将它们添加到列表时必须将它们装箱,并在检索时取消装箱。铸造以及装箱和拆箱操作都会降低性能;在您必须迭代大型集合的情况下,装箱和拆箱的效果可能非常重要。
谢谢!
这是一个更详细的解释,着眼于公共语言运行时的内部。
首先,让我们区分值类型和引用类型:
如果您不知道堆栈是什么(不要被冒犯),它是一个内存区域,用于保存方法中的局部变量和用于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
,它希望其实际值位于堆栈指向的堆位置。
.net Framework 中有两种不同的类型。
值类型,例如 int、double、single
ReferenceTypes ArrayList List 等等
ValueTypes类型的变量存储在 Stack ReferenceTyped变量存储在堆中
ValueTypes类型的变量存储 VALUE ReferenceTyped变量将 REFERENCE 存储到一个值
因此,如果您复制ValueType变量 - 存在值的真实副本,但如果您复制ReferenceType变量 - 您将获得对 SAME 变量的额外引用。
在您的问题中装箱意味着, valueType 变量(例如 int)将像引用类型变量一样被处理 - .net 将其放入一个新框中。所以它将被封装在堆中,并且会有对它的引用。
如果您希望该值再次出现在 valueType 变量中,则必须将其拆箱(将其从框中取出)。因此,该值将从堆中取出 - 并再次存储/提供给堆栈。
ArrayList 只存储对象。对于引用类型(如String
),这没有问题,但对于 ValueType (int, DateTime, ..) 是。
这些值类型需要先转换为对象,然后才能将它们存储为普通对象。这种“转换为对象”称为“装箱”,需要一点时间。
当您读回该值时,您需要从 Object 转换为int
(或任何它)。这称为“拆箱”。