C产品如何满足特定用户需求?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1048个文字,预计阅读时间需要5分钟。
选择`struct`还是`class`取决于具体的语言语义和需求:
赋值和传参时行为完全不同
这是最常踩坑的点——你以为在改副本,其实改了原对象,或者反过来。
-
struct赋值(s2 = s1)或作为方法参数传入时,会完整复制所有字段。修改s2.X不会影响s1.X -
class赋值(c2 = c1)只是复制引用地址。修改c2.X会同步反映到c1.X - 即使方法参数声明为
ref或in,struct仍可能因装箱(比如转object或接口)意外触发堆分配,带来 GC 压力和性能抖动
构造函数和字段初始化限制很硬
struct 的构造逻辑受编译器强约束,稍不注意就编译失败。
-
struct不允许定义无参构造函数(C# 10+ 允许但仅限于显式调用,且不能省略字段初始化) - 所有字段必须在每个构造路径中被显式赋值,包括
public字段和自动属性的 backing field(public int X { get; set; }算作未初始化,需在构造函数里写X = 0) -
class可自由定义无参/有参构造,字段还能带默认值(public int X = 42;),struct不允许字段初始值设定项 - 声明即初始化:
MyStruct s;合法,且s.X为0;MyClass c;合法,但c为null
继承、多态和内存位置不可混用
试图让 struct 承担类的职责,很快会撞墙。
-
struct是隐式sealed,不能被继承,也不能继承其他class或struct;只支持实现接口(但接口调用会触发装箱) -
class支持完整的继承链、虚方法、抽象成员、protected访问修饰符;struct不允许virtual、abstract、override(除重写ValueType方法外) -
struct实例通常在栈上,但嵌套在class中、作为泛型集合元素、或被装箱后,就会跑到堆上——此时它失去“栈快”的优势,还多了装箱开销 -
class总是在堆上,由 GC 管理;struct没有析构函数,也不参与 GC 生命周期
大小和场景选择有经验阈值
没有绝对标准,但超过几个关键数字,struct 就大概率变成负优化。
- 推荐上限:16 字节以内(如
Point { int X, Y; }是 8 字节)。超过这个大小,传参/赋值的复制成本可能抵消栈分配优势 - 避免含引用类型字段的
struct(如string、object、class实例),它会让结构体实际占用更大内存,且破坏“纯值”语义 - 高频创建/销毁的小对象(如数学计算中的向量、颜色)适合
struct;需要事件、资源持有(文件句柄、数据库连接)、异步状态机的对象必须用class -
Nullable<T>底层就是struct,所以int?能为null——但这只是语法糖,struct本身仍不可空
真正难的不是记住规则,而是判断“这个类型到底该不该被共享”。一旦你发现两个变量本应独立却意外联动,或者 profiling 显示大量装箱/GC,大概率是 struct 用错了地方,或者该换 class 了。
本文共计1048个文字,预计阅读时间需要5分钟。
选择`struct`还是`class`取决于具体的语言语义和需求:
赋值和传参时行为完全不同
这是最常踩坑的点——你以为在改副本,其实改了原对象,或者反过来。
-
struct赋值(s2 = s1)或作为方法参数传入时,会完整复制所有字段。修改s2.X不会影响s1.X -
class赋值(c2 = c1)只是复制引用地址。修改c2.X会同步反映到c1.X - 即使方法参数声明为
ref或in,struct仍可能因装箱(比如转object或接口)意外触发堆分配,带来 GC 压力和性能抖动
构造函数和字段初始化限制很硬
struct 的构造逻辑受编译器强约束,稍不注意就编译失败。
-
struct不允许定义无参构造函数(C# 10+ 允许但仅限于显式调用,且不能省略字段初始化) - 所有字段必须在每个构造路径中被显式赋值,包括
public字段和自动属性的 backing field(public int X { get; set; }算作未初始化,需在构造函数里写X = 0) -
class可自由定义无参/有参构造,字段还能带默认值(public int X = 42;),struct不允许字段初始值设定项 - 声明即初始化:
MyStruct s;合法,且s.X为0;MyClass c;合法,但c为null
继承、多态和内存位置不可混用
试图让 struct 承担类的职责,很快会撞墙。
-
struct是隐式sealed,不能被继承,也不能继承其他class或struct;只支持实现接口(但接口调用会触发装箱) -
class支持完整的继承链、虚方法、抽象成员、protected访问修饰符;struct不允许virtual、abstract、override(除重写ValueType方法外) -
struct实例通常在栈上,但嵌套在class中、作为泛型集合元素、或被装箱后,就会跑到堆上——此时它失去“栈快”的优势,还多了装箱开销 -
class总是在堆上,由 GC 管理;struct没有析构函数,也不参与 GC 生命周期
大小和场景选择有经验阈值
没有绝对标准,但超过几个关键数字,struct 就大概率变成负优化。
- 推荐上限:16 字节以内(如
Point { int X, Y; }是 8 字节)。超过这个大小,传参/赋值的复制成本可能抵消栈分配优势 - 避免含引用类型字段的
struct(如string、object、class实例),它会让结构体实际占用更大内存,且破坏“纯值”语义 - 高频创建/销毁的小对象(如数学计算中的向量、颜色)适合
struct;需要事件、资源持有(文件句柄、数据库连接)、异步状态机的对象必须用class -
Nullable<T>底层就是struct,所以int?能为null——但这只是语法糖,struct本身仍不可空
真正难的不是记住规则,而是判断“这个类型到底该不该被共享”。一旦你发现两个变量本应独立却意外联动,或者 profiling 显示大量装箱/GC,大概率是 struct 用错了地方,或者该换 class 了。

