C# 结构体 (struct) vs 类 (class) 本质区别 + 栈 / 堆分配
·
一、核心定性
- class :引用类型
- struct :值类型
二、栈分配还是堆分配
1. 结构体 struct(值类型)
- 局部变量:直接分配在 栈 Stack
- 作为类的字段:跟着对象一起分配在 堆 Heap(内嵌在对象内存里)
- 作为数组元素:数组在堆,结构体内嵌在数组堆内存,不是单独堆对象
- 永远不会单独在堆上新建对象,除非装箱
2. 类 class(引用类型)
- 对象实例:永远分配在 堆 Heap
- 引用变量:存地址,在 栈 Stack
一句话记
- struct 本身优先站栈;被包裹(字段 / 数组)就嵌到堆里
- class 实例永远堆上,引用永远栈上
装箱补充
把 struct 赋值给 object、接口、泛型引用类型参数 → 装箱:会在堆上创建一个包装对象,把结构体值拷贝进去,产生 GC 开销。
struct Point { public int X; }
object o = new Point(); // 装箱:栈结构体内嵌到堆包装对象
三、本质区别一览表
| 对比项 | struct 结构体 | class 类 |
|---|---|---|
| 类型分类 | 值类型 | 引用类型 |
| 内存位置 | 局部→栈;字段 / 数组→内嵌堆 | 实例永远堆,引用栈 |
| 赋值方式 | 值拷贝(复制一份全新数据) | 引用拷贝(只复制地址,共用同一个对象) |
| 继承 | 不能继承类,只能实现接口;隐式继承 ValueType | 支持单类继承、多接口继承 |
| 构造函数 | 有无参默认构造,不能自定义无参构造 | 可自定义无参、有参构造 |
| 成员默认值 | 字段必须全部赋值才能用 | 引用默认 null,值类型默认 0 |
| 析构函数 | 不能写析构 | 可以写析构~ClassName () |
| 生命周期 | 栈上自动释放,无 GC;装箱才走 GC | 全靠 GC 回收 |
| 可变性 | 建议设计成不可变 | 默认可变 |
| 初始化 | 可不用 new 直接用 | 必须 new 创建实例 |
四、赋值行为差异
struct 值拷贝
struct Point
{
public int X;
}
Point p1 = new Point();
p1.X = 10;
Point p2 = p1; // 完整拷贝一份
p2.X = 20;
// 互不影响
Console.WriteLine(p1.X); // 10
Console.WriteLine(p2.X); // 20
class 引用拷贝
class Point
{
public int X;
}
Point p1 = new Point();
p1.X = 10;
Point p2 = p1; // 只复制地址,指向同一个堆对象
p2.X = 20;
// 互相影响
Console.WriteLine(p1.X); // 20
五、什么时候用 struct?什么时候用 class?
用 struct 场景
- 数据量小(一般小于 16 字节)
- 单纯数据载体,无复杂行为
- 频繁创建、销毁,追求栈分配无 GC 开销
- 不需要继承
例:坐标、颜色、范围、配置小结构体
用 class 场景
- 数据量大、成员多
- 需要继承、多态
- 需要引用共享、修改同步
- 有复杂业务方法、生命周期管理
六、总结
- struct 是值类型,class 是引用类型
- struct 局部在栈,作为字段 / 数组内嵌到堆;class 实例永远堆,引用在栈
- struct 赋值是值拷贝,各自独立;class 赋值是地址拷贝,共用对象
- struct 不能继承类、无自定义无参构造;class 支持完整继承
- 小数据、高频分配、追求无 GC 用 struct;复杂业务、继承多态用 class
- struct 赋值给 object / 接口会装箱,逃逸到堆,产生 GC。
更多推荐



所有评论(0)