如何让每个ref struct实例拥有独立的本地数组?
解决方案
问题核心在于ref struct中的LevelInfo[]是引用类型,复制结构体时仅拷贝数组引用,导致多个实例共享同一数组内容。要实现每个实例拥有独立的嵌套状态,可通过以下两种方式解决:
方案一:基于数组的手动克隆(允许堆分配)
给Reader添加克隆方法,在复制时手动克隆整个LevelInfo数组(因LevelInfo是值类型,数组克隆会自动复制所有元素):
public ref struct Reader { private struct LevelInfo { private ushort indentation; public int Indentation { readonly get => indentation; set { ArgumentOutOfRangeException.ThrowIfLessThan(value, 0); ArgumentOutOfRangeException.ThrowIfGreaterThan(value, 0b00111111_11111111); indentation = (ushort)value; } } } private LevelInfo[] levels; // 其他状态字段(如当前读取位置等) public Reader(int maxLevels) { levels = new LevelInfo[maxLevels]; // 初始化默认状态 } // 手动实现克隆逻辑 public Reader Clone() { var cloned = this; // 克隆数组,生成独立的新数组实例 cloned.levels = (LevelInfo[])this.levels.Clone(); // 同步其他需要复制的状态字段 // 示例:cloned.CurrentOffset = this.CurrentOffset; return cloned; } void SomeMethod() { levels[0].Indentation = 1; } }
使用时需调用Clone方法而非直接赋值:
var original = new Reader(10); var clonedReader = original.Clone(); // 修改clonedReader的levels不会影响original
方案二:基于Span的栈分配(无堆分配,适合预定义最大层级)
如果嵌套层级的最大值可提前确定,用Span<LevelInfo>替代数组,利用栈分配实现无堆内存开销的独立状态:
public ref struct Reader { private struct LevelInfo { private ushort indentation; public int Indentation { readonly get => indentation; set { ArgumentOutOfRangeException.ThrowIfLessThan(value, 0); ArgumentOutOfRangeException.ThrowIfGreaterThan(value, 0b00111111_11111111); indentation = (ushort)value; } } } private Span<LevelInfo> levels; // 其他状态字段 public Reader(int maxLevels) { // 在栈上分配固定长度的LevelInfo数组 levels = stackalloc LevelInfo[maxLevels]; // 初始化默认状态 } public Reader Clone() { var cloned = new Reader(levels.Length); // 将当前层级状态复制到新的栈分配内存中 this.levels.CopyTo(cloned.levels); // 同步其他状态字段 return cloned; } void SomeMethod() { levels[0].Indentation = 1; } }
这种方式避免了堆内存分配,但需注意栈内存容量限制,maxLevels不能过大(一般建议不超过千级),否则会触发栈溢出。
内容的提问来源于stack exchange,提问作者ygoe
相关产品推荐
相关产品推荐

