CSharp数据类型转换之拆箱装箱
本篇目标:能说出什么是装箱、什么是拆箱,能自己写一段装箱拆箱的代码,并且知道拆箱时最容易踩的坑。
一、先补个前提:值类型 和 引用类型
用一个生活比喻分清两类类型
想象你有一笔钱(数据):
- 值类型:把现金直接塞进自己的口袋。钱就在口袋里,要用直接掏。比如
int、double、bool、float,它们把「值本身」直接放在变量里。 - 引用类型:口袋里的不是钱,是一把储物柜的钥匙。真正的钱放在储物柜里,你要用钱,得先拿着钥匙去开柜子。比如
string、object,它们变量里放的是「地址」,真正的数据在别处。
一句话记:值类型装的是值本身,引用类型装的是地址。
| 分类 | 常见类型 | 变量里装的是 |
|---|---|---|
| 值类型 | int double bool float long |
值本身 |
| 引用类型 | string object 自定义类 |
地址(钥匙) |
还记得上一篇学的 int、double 吗?它们都是值类型。而 string(字符串)是引用类型,object 是所有类型的「老祖宗」,它俩都是引用类型。记住这两个就够用了。
二、什么是装箱(Boxing)
1 | int value = 100; // 值类型,装在变量里 |
装箱背后发生了什么?
装箱时,电脑做了两步:
- 在「堆」(储物柜区)里开辟一块新空间,专门放这个盒子。
- 把
value的值 100 复制一份,放进盒子里,再把盒子的地址交给box。
所以装箱不是「把 int 变成 object」,而是「复制一份值,包成一个 object」。原来的 value 还是 int,一点没变。
装箱是隐式的,也就是你什么都不用写,直接赋值 object box = value;,电脑自动帮你装箱。
三、什么是拆箱(Unboxing)
1 | object box = 100; // 先装箱:盒子里装的是 int 类型的 100 |
拆箱背后发生了什么?
拆箱也分两步:
- 先检查盒子里装的值,类型是不是
int。 - 如果是,就把盒子里的 100 复制出来,交给
value;如果不是,直接报错(这个坑下一节重点讲)。
注意那个 (int) 不能省,它是「拆箱」的标志,告诉电脑我要按 int 来取。
装箱是隐式的(自动),拆箱是显式的(必须写 (类型))。这是它俩最明显的区别。
四、为什么要拆箱装箱?有什么用?
老集合 ArrayList 的例子
C# 早期的集合叫 ArrayList,它的「装东西」方法长这样(参数是 object):
1 | using System.Collections; |
因为 ArrayList 只能装 object,所以塞进去自动装箱,取出来手动拆箱。这就是拆箱装箱的由来。
不过现在写新代码,我们几乎不用 ArrayList 了,都用泛型集合(比如 List<int>),泛型不需要装箱拆箱。但你仍然会在老代码、某些 API、反射里碰到装箱拆箱,所以概念必须懂。
五、拆箱最大的坑:类型必须完全对上
1 | object box = 100; // 盒子里装的是 int |
为什么 int 不能直接拆成 double?
因为拆箱要求类型完全一致:盒子装的是 int,你就只能用 int 去拆,用 double 去拆会抛 InvalidCastException 异常(程序崩溃)。
那想把它变成 double 怎么办?分两步:先拆回 int,再让 int 隐式转成 double:
1 | object box = 100; |
记住口诀:拆箱要同型,先拆回原类型,再谈转换。
再看一个会翻车的例子:
1 | object box = 3.14; // 盒子里装的是 double |
类型一旦对不上,程序直接崩,所以拆箱前一定想清楚盒子里到底装的是啥。
六、性能提醒:别没事瞎装箱
什么时候容易不小心装箱?
比如用 + 拼接字符串时,数值会先装箱再拼接。虽然单次看不出差别,但循环几十万次就有感觉了。新手阶段先记住这个意识即可,等学到泛型、集合再回头看会更明白。
七、上位机里的场景
1 | class Program |
结合之前学的模拟量换算
拆出来的原始值,还能接上之前学的换算:
1 | object tagRaw = 13824; // int 装箱 |
八、小结
理解值类型装的是值本身,引用类型装的是地址
能写出 object box = 100; 这种装箱代码
知道拆箱必须写 (类型),而且类型要完全一致
知道 object box = 100; double d = (double)box; 会报错
九、能跑起来的小练习
点我看完整练习代码
1 | using System; |




