C# 装箱、拆箱 底层原理

装箱 = 值类型 → 引用类型(堆上分配)

拆箱 = 引用类型 → 值类型(堆上取数据)

它们是隐式类型转换,底层由 CLR 自动完成。

1. 先搞懂两个基础:栈 和 堆

C# 内存分两块:

  • 栈(Stack) :存值类型(int、double、struct、bool),轻量、自动释放、速度极快。
  • 堆(Heap) :存引用类型(string、object、类实例),需要 GC 管理、速度慢。

值类型本身只在栈上,根本不在堆里。

2. 装箱(Boxing)底层到底做了什么?

cs 复制代码
int a = 10;        // 栈上的值类型
object obj = a;    // 这里发生了 **装箱**

底层 3 步动作(CLR 自动执行)

  • 在托管堆上分配一块新内存 大小 = 值类型本身大小 + 对象头(Type Handle + Sync Block)

  • 把栈上的值类型 复制 到堆上 栈上的 a = 10 原样拷贝到堆内存里。

  • 返回堆上对象的地址 赋值给 obj 引用变量。

内存变化图

cs 复制代码
【栈】             【堆】
a = 10        →    分配新对象
                 拷贝 10 进去
obj = 堆地址  ←   返回堆地址

一句话

装箱 = 堆上新建对象 + 栈数据拷贝到堆

3. 拆箱(Unboxing)底层做了什么?

cs 复制代码
object obj = 10;   // 先装箱
int b = (int)obj;  // 这里发生 **拆箱**

底层 3 步动作

  • CLR 检查堆上对象 确认它确实是被装箱的 int ,不是别的类型,否则直接抛 InvalidCastException

  • 获取堆中值类型数据的地址 找到堆里那个真正的 10

  • 把数据 复制回栈 拷贝到栈上的变量 b 里。

内存变化

cs 复制代码
【堆】          【栈】
装箱的 int  →  取出值 10
              复制到 b
b = 10

一句话

拆箱 = 类型检查 + 堆数据拷贝回栈

4. 性能代价(为什么要少用装箱?)

装箱的成本(非常高)

  1. 堆内存分配

  2. 数据拷贝

  3. GC 压力(堆对象多了,GC 就要干活)

拆箱的成本

  1. 类型检查

  2. 数据拷贝

对比

  • 直接赋值:1 个 CPU 指令

  • 装箱:几十~上百个 CPU 指令

频繁装箱 = 程序明显变慢。

5. 最容易触发装箱的 3 种代码

1)把值类型扔进 object / 非泛型集合

cs 复制代码
int a = 10;
object b = a;       // 装箱

ArrayList list = new ArrayList();
list.Add(10);       // 装箱!

2)值类型调用 ToString () / GetHashCode () 等重写方法

cs 复制代码
int a = 10;
string s = a.ToString(); // **不装箱**(因为重写了)

int a = 10;
a.GetType();         // 装箱!(没重写,会转成 object)

3)字符串拼接 + 值类型

cs 复制代码
int a = 10;
string s = "年龄:" + a;  // 装箱!

C# 会把 a 转成 object 再拼接。

6. 如何避免装箱?

  • 用泛型集合List<int> 而不是 ArrayList
  • 避免值类型转 object
  • 字符串拼接用 $""string.Format 不会装箱(C# 编译器优化)
  • 用重载方法,不要传 object

装箱

  • 栈 → 堆

  • 分配堆内存

  • 拷贝值类型数据

  • 返回引用

  • 性能开销大

拆箱

  • 堆 → 栈

  • 类型检查

  • 拷贝数据回栈

  • 必须类型完全匹配

本质

装箱拆箱 = 内存拷贝 + 栈堆转换

总结

  • 装箱 :值类型 → 引用类型,堆分配 + 数据拷贝
  • 拆箱 :引用类型 → 值类型,类型检查 + 数据拷贝回栈
  • 成本:装箱 > 拆箱 > 普通赋值
  • 优化:用泛型、避免转 object、避免频繁字符串拼接
相关推荐
牛哇网络工作室20 小时前
UnityHDRP写实数字人全流程基础5—语音输入和语音识别
android·unity·c#·游戏引擎·aigc·语音识别·xcode
zlinear数据采集卡1 天前
数据采集卡从入门到精通(9):分辨率与精度——16位卡不等于1/65536的精度
开发语言·数据库·fpga开发·开源·c#
深澈Vincel1 天前
我用 C# + WebView2 做了一个针对“流氓软件”的“一键清理工具“:架构实践与踩坑记录
c#
zlinear数据采集卡1 天前
数据采集卡从入门到精通(7):流水线型ADC——级级接力,高速与高精的平衡术
开发语言·arm开发·嵌入式硬件·fpga开发·c#
kingwebo'sZone1 天前
C# 根据公式计算的方法
开发语言·c#
阿部多瑞 ABU1 天前
告别手工核图:基于 .NET + MuPDFCore 的 CAD 等轴测 PDF 材料表提取与新旧版本对比实战
后端·算法·ui·pdf·c#
条形码D2 天前
DRG综合管理分析系统-目前已经支持CHS-DRG3.0
c#·drg·3.0分组器·drg分组器·chs-drg3.0分组器
Alex Gram2 天前
数据库同步工具PanguSync图文教程
java·mysql·postgresql·sqlserver·c#·数据库同步软件·数据库同步工具
头茬韭菜2 天前
第 10 篇:「Fluss 实战案例集」—— 完整解决方案与最佳实践
c#·linq·fluss
淡海水3 天前
01-05-运行时-JIT优化全景-内联去虚拟化边界检查消除
windows·c#·虚拟化·jit·内联·边界检查消除