C#12中ref struct主构造函数ref-like类型参数实例成员使用报错CS9110原因?
问题场景
升级代码至C#12并尝试在合适场景使用主构造函数时,遇到以下问题:在一个接收ReadOnlySpan<char>(属于ref-like类型)作为构造参数的ref struct中,在实例方法MoveNext里直接访问主构造参数value时,触发CS9110错误:Cannot use primary constructor parameter that has ref-like type inside an instance member。
代码示例:
/// <summary>An utility like String.Split, but for Spans. This should avoid allocations.</summary> internal ref struct CharSpanSplitter(ReadOnlySpan<char> value, char separator) { // Uncomment to make the error go away //private ReadOnlySpan<char> value = value; public ReadOnlySpan<char> Current { get; private set; } public bool MoveNext() { // CS9110: Cannot use primary constructor parameter that has ref-like type inside an instance member. if (value == ReadOnlySpan<char>.Empty) { return false; } // CS9110: Cannot use primary constructor parameter that has ref-like type inside an instance member. var index = value.IndexOf(separator); if (index == -1) { // CS9110: Cannot use primary constructor parameter that has ref-like type inside an instance member. Current = value; // CS9110: Cannot use primary constructor parameter that has ref-like type inside an instance member. value = ReadOnlySpan<char>.Empty; return true; } // CS9110: Cannot use primary constructor parameter that has ref-like type inside an instance member. Current = value[..index]; // CS9110: Cannot use primary constructor parameter that has ref-like type inside an instance member. value = value[(index + 1)..]; return true; } }
问题解答
1. 为何直接访问主构造参数不被允许?
主构造参数的默认作用域仅局限于构造函数执行阶段,本质上属于构造函数的局部变量。而ReadOnlySpan<char>这类ref-like类型的核心约束是:不能存在超出其声明生命周期的引用,否则会引发悬垂引用导致内存安全问题。
如果允许在实例方法中访问主构造的ref-like参数,编译器无法保证该参数的生命周期在实例方法调用时仍然有效——构造函数执行完成后,主构造参数的生命周期理论上已经结束,实例方法调用时再访问它,可能会引用到已经失效的内存区域。因此C#编译器通过CS9110错误禁止这种操作,从语法层面规避潜在的内存风险。
2. 赋值给私有字段后错误消失的原因?
当将主构造的ref-like参数赋值给ref struct的私有字段时,该字段的生命周期与ref struct实例本身绑定。由于ref struct是栈分配的类型,编译器可以完整跟踪实例的生命周期,确保实例方法执行时,字段中的ref-like类型引用始终处于有效状态,不会出现悬垂引用的问题。因此编译器允许这种访问方式,错误自然消失。
内容的提问来源于stack exchange,提问作者Steven Liekens

