装箱/拆箱与类型转换之间有什么区别?


74

装箱/拆箱与类型转换之间有什么区别?

通常,这些术语似乎可以互换使用。


1
我必须阅读这里的前2个答案,才能很好地了解它们之间的区别,但是我想我现在已经掌握了很牢固的知识
赛马会

Answers:


48

拳击是指转换不可为空值类型的成引用类型或值类型的一些接口,它实现(说的转化intIComparable<int>)。此外,基础值类型到可空类型的转换也是装箱转换。(注意:关于此主题的大多数讨论都将忽略后两种转换。)

例如,

int i = 5;
object o = i;

转换i为type的实例object

拆箱是指显式转换从实例objectValueType到一个不可为空值类型,接口类型的转换到非可空值类型(例如,IComparable<int>int)。此外,可为空的类型到基础类型的转换也是拆箱转换。(注意:关于此主题的大多数讨论都将忽略后两种转换。)

例如,

object o = (int)5;
int i = (int)o;

将装箱的整数转换o为type的实例int

类型转换是将表达式显式转换为给定类型。从而

(type) expression

显式转换expression为类型的对象type


谢谢。关于这些事情,似乎没有标准的语言。

11
关于这些事情,有非常标准的语言。请参阅C#语言规范,ECMA#334。
杰森

15
为了清楚起见,当杰森说“将基础值类型转换为可空类型也就是装箱转换”时,他所说的“可空类型”是指引用类型,可以将其设置为空,因此他使用术语“可为空”。他的意思不是Nullable<T>类型。将值类型强制转换为Nullable<T>(aka T?)不是装箱/拆箱的示例,因为未创建引用类型。例如int? x = 5是一个类型转换,不是拳击的一个例子。
drwatsoncode 2013年

1
“例如int?x = 5是类型转换而不是装箱的示例。” 为了完整起见,这是IMPLICIT类型强制转换的示例,与Jason提供的显式类型强制转换示例不同。
almulo 2015年

如果可以投射,则可以取消投射吗?也就是说,内存是在堆上释放的吗?
BKSpurgeon '17

29

装箱和拆箱是类型转换的子集。装箱是将值类型视为引用类型的行为(实际上,这涉及将值类型的内容(从堆栈中)复制到堆中,并将引用返回给该对象)。这允许在期望兼容的引用类型的任何地方传递值类型。它还允许对值类型执行虚拟方法调用和引用类型的其他功能。取消装箱是此操作的相反操作(从装箱的对象中取回值类型)。

类型转换是用于从特定类型的变量到另一类型的任何类型的转换的术语。这是一个更广泛的概念。

几分钟前,我回答了一个有关这一区别的相关问题。总而言之,我对C#类型转换运算符生成的不同类型的IL指令进行了分类:

  1. 装箱(boxIL指令)和拆箱(unboxIL指令)
  2. 通过继承层次结构进行转换(如dynamic_cast<Type>C ++中一样,使用castclassIL指令进行验证)
  3. 基本类型之间的转换(如static_cast<Type>C ++中一样,对于基本类型之间的不同类型的转换,有很多IL指令)
  4. 调用用户定义的转换运算符(在IL级别,它们只是对适当op_XXX方法的方法调用)。

3
那对我没有太大帮助。我想我将在互联网上进行一些研究,以找到要比较的特定定义。

1
我不会说装箱/拆箱完全是铸造的一个子集。它们是关于将值类型移动到引用类型的,因此可以在其他位置更改/存储它们。强制转换是重新中断引用类型的过程。
Simeon朝圣者

@Simeon:从语言设计的角度来看,装箱的int只是一种不同的类型,并且您正在不同的类型之间进行转换。
mmx

其中,a将其视为相同的类型(例如,它们都是int的),但是存储int的方式不同。但是从C的背景来看,它只是指向我的指针。
Simeon朝圣者

@Simeon:这是思考拳击的错误方式。可能是直接考虑堆栈/堆区别的人工产物。正如我所说,从语义上讲,它只是将值类型转换为具有引用特征的类型。请注意,与不同int*,它仍然是不可变的。堆上的内存实际上并不是4个字节。它具有类型信息并在其上调用虚拟方法:因此object o = 5; string s = o.ToString();起作用。这是不可能的简单的int*。在C ++中,这涉及vtables,而拳击也必须处理这些东西。
mmx

20

装箱是用于将值类型(int,double,float,Guid等)转换为引用类型(System.Object,System.String等)的术语。执行此装箱操作会在堆上分配内存(垃圾回收器最终将需要回收该内存)。取消装箱是此过程的逆过程,它采用引用类型并将其转换为值类型。

强制转换采用一种类型(例如System.Object)并将其视为另一种类型(例如System.String)。

当您在C#中装箱某些东西时,会将其转换为另一种类型。不同之处在于,当创建新的引用类型时,它会分配额外的内存

底线:装箱是一种特殊的类型转换,它将值类型转换为引用类型,这需要分配新的引用类型。


2
堆是一个实现细节。
杰森

1
那个“堆”的细节实际上可以帮助我更好地理解它!

5
不太正确。首先,它不一定是System.Object,而实际上是任何引用类型。例如也IComparable<int> i = 5导致拳击。其次,boxing不是唯一需要内存分配的演员表。用户定义的强制转换也可以这样做。第三,正如Jason所指出的,堆是实现细节。重要的是通过引用类型语义而不是在其分配位置来处理值类型
mmx

1
有时候,我认为程序员过于腐。这是那些时代之一。
Judah Gabriel Himango,2009年

2
老实说,当发布者只是询问铸造和拳击之间的区别时,我们是否需要就堆是实现细节进行辩论?我的回答是正确的-CLR在堆上分配内存以进行装箱。您虽然正确地指出,但堆正确地指出了实现的细节。我可以反驳说:“但这是当前的实现,值得注意的,并且在可预见的将来不太可能改变。” ---老实说---我们正用这种脚的玩笑变成愚蠢的人。
Judah Gabriel Himango,2009年

4

装箱/拆箱和类型转换是两个不同的操作,但是它们使用相同的语法。

仅当谈论它的人不知道实际情况时才可以互换使用它们。

装箱将值类型存储为堆上的对象,而装箱则从对象中读取值。您只能将值取消装箱,因为它是确切的类型。

强制转换是在将基本类型转换为另一种基本类型(如从intlong),或更改引用的类型(如从List<int>IEnumerable<int>)时进行的。


1
知道这一点。我问的是什么区别!

1
谢谢!那就是我想要的!

3

装箱意味着将值类型变量(即整数)转换为引用类型。使用类型转换,取消装箱是相反的操作。简而言之,在.NET世界中,一切都源自“对象”类型。

例如(C#示例):

int myInt = 0;                 // original variable (unboxed to begin with)
object boxed = myInt;          // box it up
int myIntUnBoxed = (int)boxed; // and unbox it again using type casting

这样做的好处是类型系统的统一,允许将值类型视为引用类型。本文对装箱/拆箱有更深入的了解。


基本语言是否不总是允许将值/引用类型视为相同?这是C#特定的东西吗?

您可以说装箱/拆箱的想法来自基本语言(即VB.NET之前的版本)。它不限于C#,因为相同的概念可以应用于VB.NET或针对.NET运行时的任何其他语言。自从我用基本语言编码以来已经有很长时间了...啊,回忆。
ajawad987
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.