值类型存数据,引用类型存地址;struct通常栈分配、赋值深拷贝、不可为null;class堆分配、赋值浅拷贝、默认null;选择需综合装箱、线程共享、泛型约束等场景。

值类型直接存数据,引用类型只存地址——这是所有行为差异的起点。
struct 和 class 的内存分配位置不同
局部变量声明的 struct 实例(如 Point p = new Point(1, 2);)通常整个结构体数据都落在栈上;而 class 实例(如 Person p = new Person();)对象本体一定在堆上,栈上只存一个 4 或 8 字节的引用(指针)。
- 例外:当
struct是某个class的字段时,它会随类一起被分配在堆上(不是栈) - 栈内存自动释放,无 GC 开销;堆内存由垃圾回收器管理,有延迟和不确定性
- 频繁创建小
struct(如Vector2)可显著降低 GC 压力;但大struct(比如超过 16 字节)栈拷贝开销反而升高
赋值时的行为差异:值拷贝 vs 引用拷贝
这是最常踩坑的地方——表面写法一样,底层语义完全不同。
-
struct赋值是深拷贝:s2 = s1后修改s2.X不会影响s1.X -
class赋值是浅拷贝:c2 = c1后c2和c1指向同一块堆内存,改一个就等于改了另一个 - 传参也一样:把
struct传给方法,默认复制整块数据;class传参只复制引用,方法内修改对象状态会反映到调用方
默认值、null 和构造函数约束
这些限制都源于“值类型必须始终有确定值”这一设计前提。
-
struct变量不能为null(除非显式声明为Point?),默认就是所有字段为零值(X=0, Y=0) -
class变量默认是null,不 new 就用会抛NullReferenceException -
struct不允许定义无参构造函数(C# 10+ 允许init成员但仍有字段初始化强制要求),所有字段必须在构造中赋值 -
class可自由定义任意构造函数,字段也能带默认值(public int Count = 1;)
真正难的不是记住区别,而是判断什么时候该用哪个——别光看“小数据用 struct”,还要看是否会被装箱、是否跨线程共享、是否要作为泛型约束(where T : struct)、甚至是否会被序列化成 JSON(某些库对 struct 的 null 处理更严格)。这些细节一漏,运行时表现就可能和预期南辕北辙。










