struct是值类型,不可继承非接口类型,字段初始化限于常量表达式,含引用类型字段仍为值类型;适用小体积、短生命周期、代表逻辑值的场景,ref struct禁止逃逸至堆,相等性需谨慎实现。

struct 定义必须用 struct 关键字,不能带继承
struct 是值类型,编译器强制要求它不能从其他类或结构体继承(除了隐式继承 System.ValueType),也不能被其他类型继承。一旦你写 struct Point : ICloneable 是合法的(实现接口可以),但 struct Point : BaseStruct 会直接报错 CS0527: 'Point': A struct cannot derive from type 'BaseStruct'。
常见错误是下意识把 class 的写法套过来,比如加 virtual 方法、试图写无参构造函数(C# 10+ 之前不允许显式定义无参构造)、或者在字段初始化里调用实例方法——这些都会触发编译错误。
-
struct中字段初始化只能是常量表达式,比如int x = 0;合法,int y = GetDefault();不合法 - 显式定义构造函数时,必须初始化所有字段(包括自动属性背后的私有字段),否则报
CS0188: The 'this' object cannot be used before all of its fields are assigned - 如果结构体包含引用类型字段(如
string或List<int></int>),它仍是值类型——拷贝时只复制引用,不是深拷贝
什么时候该用 struct 而不是 class
核心判断标准就一条:这个类型是否「逻辑上代表一个值」,且「体积小、生命周期短、不常被多处共享」。比如坐标点、颜色、时间间隔、小尺寸元组。
典型误用场景是把业务实体(如 OrderItem、UserSummary)定义成 struct——它们往往字段多、可能含集合、需要虚方法或多态,一传参就悄悄拷贝,既浪费内存又容易引发状态不一致。
- 推荐用
struct:小于 16 字节、无引用类型字段、无虚成员、不需多态、高频创建/传递(如数学计算中的Vector2) - 必须用
class:需要继承、实现多态、含事件或IDisposable、字段含大对象(如byte[])、要被 DI 容器管理 - 性能提示:struct 在栈上分配快,但传参或赋值时整块拷贝;class 分配稍慢但传参只传引用——别光看“栈更快”,得看实际数据流动路径
ref struct 是什么,为什么不能离开栈
ref struct 是 C# 7.2 引入的特殊结构体,本质是「禁止逃逸到堆」的约束类型,比如 Span<t></t> 和 ReadOnlySpan<t></t> 都是它的典型应用。它不能作为 class 字段、不能实现接口(除 IDisposable)、不能装箱、不能用在异步方法中——任何可能导致它被提升到堆的操作都会被编译器拦截。
错误现象:把 Span<int></int> 存进 class 的字段里,会立刻报 CS8345: Field or auto-implemented property cannot be of type 'Span<int>' unless it is declared as 'static' or 'const'</int>。
-
ref struct只能作为局部变量、方法参数或返回值(且返回值不能是异步方法或迭代器) - 它常用于高性能场景(如零分配字符串解析、内存池切片),但代价是使用边界极严,不是“更快的 struct”,而是“受控的栈内存”
- 别为了“看起来高级”而用
ref struct——99% 的业务代码不需要它,强行套用只会卡在编译阶段
struct 的相等性默认行为容易踩坑
C# 中 struct 默认使用「逐字段比较」做相等判断,但这只对字段都是值类型或不可变引用类型(如 string)才可靠。一旦含可变引用类型(如 List<int></int>),a == b 为 true 并不代表两个 struct 的列表内容相同——因为比较的是引用地址,不是内容。
更隐蔽的问题是:如果你重载了 == 和 !=,却没同步实现 IEquatable<t></t> 和 GetHashCode(),放进 Dictionary<tkey tvalue></tkey> 或 HashSet<t></t> 就会出错——查找失效、重复插入、甚至死循环。
- 安全做法:所有公开暴露的 struct,只要重载
==,就必须实现IEquatable<t></t>,并确保GetHashCode()基于相同字段计算 - 简单省事方案:用
record struct(C# 10+),它自动生成相等性和哈希逻辑,且默认不可变 - 调试技巧:用
Console.WriteLine(a.Equals(b))比a == b更能看出底层行为,因为前者走的是IEquatable<t>.Equals()</t>或Object.Equals()
struct 看似简单,但值语义、拷贝行为、相等性、内存约束这四层嵌套在一起,稍不注意就会在运行时露出破绽。最常被忽略的是字段可变性带来的相等性断裂,以及把 ref struct 当普通 struct 用导致编译失败——这两处问题往往要等到集成测试或压测阶段才浮现。









