如何让自定义ref struct枚举器在foreach循环中隐式生效?
解决Span自定义ref struct枚举器被自带枚举器覆盖的问题
要让编译器优先识别你的自定义Vector
方案一:用自定义ref struct包装Span
把目标Span
代码示例
首先定义自定义的Vector枚举器:
public ref struct VectorEnumerator<T> where T : unmanaged { private readonly Span<T> _targetSpan; private int _currentOffset; public VectorEnumerator(Span<T> span) { _targetSpan = span; _currentOffset = -Vector<T>.Count; // 初始位置设为矢量长度的负数,第一次MoveNext后跳到0 } // 实现foreach约定的MoveNext方法 public bool MoveNext() { _currentOffset += Vector<T>.Count; return _currentOffset < _targetSpan.Length; } // 实现foreach约定的Current属性 public Vector<T> Current { get { if (_currentOffset < 0 || _currentOffset >= _targetSpan.Length) throw new InvalidOperationException("枚举器位置无效"); return new Vector<T>(_targetSpan.Slice(_currentOffset, Vector<T>.Count)); } } }
然后定义包装Span的ref struct:
public ref struct VectorizedSpan<T> where T : unmanaged { private readonly Span<T> _innerSpan; public VectorizedSpan(Span<T> span) => _innerSpan = span; // 返回自定义枚举器,编译器foreach会识别这个方法 public VectorEnumerator<T> GetEnumerator() => new VectorEnumerator<T>(_innerSpan); // 加个隐式转换,方便直接从Span<T>转成包装类型 public static implicit operator VectorizedSpan<T>(Span<T> span) => new VectorizedSpan<T>(span); }
使用方式
// 示例:用stackalloc创建Span Span<int> intSpan = stackalloc int[128]; // 直接转成VectorizedSpan后遍历 foreach (var vec in (VectorizedSpan<int>)intSpan) { // 这里的vec就是Vector<int>实例,直接做矢量运算 var result = vec * new Vector<int>(2); // ...后续处理 } // 或者借助隐式转换直接赋值后遍历 VectorizedSpan<int> vecSpan = intSpan; foreach (var vec in vecSpan) { // ... }
方案二:手动调用枚举器(不推荐,不够优雅)
如果不想包装类型,可以直接手动创建枚举器实例,用while循环替代foreach,但这种方式丢失了foreach的语法糖优势:
Span<int> intSpan = stackalloc int[128]; var enumerator = new VectorEnumerator<int>(intSpan); while (enumerator.MoveNext()) { var vec = enumerator.Current; // ...处理逻辑 }
关键注意点
- 因为
Vector<T>要求元素类型T是非托管类型(unmanaged),所以你的枚举器和包装结构体都要加where T : unmanaged约束。 - 自定义ref struct枚举器必须严格遵循foreach的约定:要有
bool MoveNext()方法和Current属性,不需要实现IEnumerator<T>接口(这也是你要的无堆分配效果)。
内容的提问来源于stack exchange,提问作者ThatGuy
相关产品推荐
相关产品推荐

