ref struct 是编译器强制绑定栈帧的内存契约,必须显式声明为 ref struct,禁止继承、装箱、泛型容器存储、类字段、async/lambda/yield 捕获及接口实现,仅限当前栈帧内安全使用。
ref struct 不是“能放栈上的 struct”,它是编译器强制绑定到当前栈帧的内存契约——一旦声明,它就拒绝一切可能逃逸的操作,包括 async、lambda、泛型容器、类字段等。用错不是性能差,而是直接编译失败。
ref struct 声明必须带 ref 关键字,不能省略或后置
编译器不接受
这种写法,哪怕内部只含
或一个
字段也不行。必须显式写成
,否则报
(A ref struct cannot be used as a type argument)或类似错误。
已有普通
直接加
修饰 → 编译失败,这不是语法糖,是全新类型类别
不能继承任何类型,连
都不能实现(接口实现会隐式允许装箱/堆引用)
命名时别用
当后缀误导自己,比如
—— 它不是“带 ref 的 struct”,而是“ref struct”
ref struct 中的 ref 字段必须手动绑定有效地址
字段不是自动初始化的引用,它本质是一个栈地址槽,未赋值就读取会触发
;且只能绑定到当前栈帧内确定存活的变量。
声明语法是
,
(空格非法)会编译报错
必须在构造函数中用
表达式显式赋值:
读取前建议用
检查,尤其在复杂初始化路径中
不能绑定到方法返回的临时值,例如
→ 编译器报 “ref safety violation”
ref struct 不能逃逸出当前栈帧,async/lambda/yield 是高危区
它的生命周期硬绑定到声明它的那个栈帧。只要变量可能被“带走”,编译器立刻拦截——返回、存字段、传给异步/委托/迭代器,全都不行。
C知道
CSDN推出的一款AI技术问答工具
下载
→ 报
→ lambda 捕获导致逃逸,编译失败
→ 只要
不离开该循环体,就合法
→ 合法,但返回的必须是当前栈帧内有效的内存(如局部
或参数传入的
)
C# 13 起支持
泛型约束
ref struct 终于可以进泛型了,但前提是显式约束,且调用链全程保持栈安全。它仍不能进
、
或任何默认泛型容器。
正确用法:
错误用法:
→ 编译失败,泛型定义没加约束,且
本身是堆类型
不能作为
字段:
→ 报
(A ref struct cannot be used as a field)
不能装箱:
→ 编译拒绝,无运行时妥协空间
真正难的不是写对语法,而是判断“这个数据是否真的适合栈上分配”:它是否生命周期严格限定在当前同步方法内?是否会被异步暂停、闭包捕获、状态机保存、或跨方法返回?只要有一个“可能”,就得换方案——比如改用
+
。
struct MyRefStructReadOnlySpanref intref struct MyRefStructCS8342structrefref structIDisposableRefMyStructRefrefNullReferenceExceptionprivate ref int _value;private ref intref_value = ref source;Unsafe.IsNullRef(ref _value)_value = ref GetLocalInt();async Task Foo() { var x = new MyRefStruct(); await Task.Delay(1); }CS8350Task.Run(() => { var x = new MyRefStruct(); })foreach (var item in list) { ref struct s = ...; DoSomething(s); }spublic ref Span GetSpan() => ref _span; stackallocrefwhere T : ref structTaskListpublic class Pool where T : ref struct { ... } ListList<>classpublic MyRefStruct Field;CS8342object o = new MyRefStruct();MemoryArrayPool.Shared 