如何在不将struct设为readonly的前提下,避免使用in参数的性能损耗?
这个问题问到点子上了——C# 7.2里的in参数和readonly struct确实是一对互补但有约束的特性,想要同时攥住「引用传递无拷贝」「编译时禁止参数重赋值」「能修改struct字段」这三个好处,咱们可以分两种场景来解决:
先理清核心矛盾
首先快速回顾下背景,帮你明确痛点:
in参数:按引用传递,编译时禁止给参数本身赋值(比如param = new Coord()),但对于可变struct,编译器会生成防御性拷贝——相当于你拿到的是拷贝后的实例,修改字段不会影响原对象,还平白多了拷贝开销。readonly struct:完美解决防御性拷贝问题,但要求所有字段都是readonly,自动属性也得是只读的,直接封死了修改字段的可能。
解决方案1:用
readonly struct配合引用类型字段实现“可变” 如果你的struct可以用引用类型来承载可变状态,那就能绕开readonly struct的字段只读限制。比如把可变的数值存在一个readonly的数组里(数组是引用类型,字段本身只是一个固定的引用,不会被修改):
public readonly struct MutableCoord { // 字段是readonly的,但数组内部元素可以修改 private readonly int[] _coords; public int X { get => _coords[0]; set => _coords[0] = value; // 修改数组元素,不改变字段本身的引用 } public int Y { get => _coords[1]; set => _coords[1] = value; } public MutableCoord(int x, int y) { _coords = new int[] { x, y }; } }
然后用in参数传递这个struct时:
public void UpdateCoords(in MutableCoord coord) { // 编译错误!in参数禁止重赋值,完美满足你的编译时检查需求 // coord = new MutableCoord(0, 0); // 可以正常修改属性,且不会触发防御性拷贝(因为是readonly struct) coord.X += 10; coord.Y += 10; }
这个方案完全命中你的所有需求:
- 继承
in参数的编译时检查:不能给参数重赋值 - 无防御性拷贝:因为是
readonly struct,编译器确认不会修改字段引用,直接按引用传递 - 可以修改struct的状态:通过引用类型字段间接实现,效果和直接改字段一致
解决方案2:用
ref参数+代码约定(或自定义分析器) 如果你不想引入引用类型的额外开销,想保持struct纯粹的“值类型”特性,那可以直接用ref参数——它本身就是按引用传递无拷贝,也能修改字段,但默认允许给参数重赋值。
想要实现编译时禁止重赋值的话,有两个办法:
- 团队内部约定:不在方法内给
ref参数赋值 - 编写Roslyn分析器:自定义规则检查方法内是否对指定
ref参数进行了赋值操作,触发编译警告/错误
示例代码:
public struct Coord { public int X, Y; } public void UpdateCoords(ref Coord coord) { // 如果有自定义分析器,这行会触发编译错误 // coord = new Coord(0, 0); // 可以正常修改字段,全程无拷贝 coord.X += 10; coord.Y += 10; }
这个方案更直接,适合对性能极致追求的场景,缺点是需要额外的约定或工具来实现编译时检查。
总结
如果要严格满足所有需求且不依赖额外工具,优先选方案1;如果追求struct的纯粹值类型特性,方案2配合代码约定或分析器是更好的选择。
内容的提问来源于stack exchange,提问作者AustinWBryan
相关产品推荐
相关产品推荐

