You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中使用Range语法优化foreach时迭代器方法报错的解决办法

解决Range枚举器在迭代器方法中无法使用的问题

问题场景

我尝试将代码中的foreach (var i in Enumerable.Range(0, _gridCount))替换为更简洁的foreach (var i in 0.._gridCount),但替换后抛出错误:

'foreach' statement cannot operate on enumerators of type 'RangeEnumerator' in 'async' or iterator methods because 'RangeEnumerator' is a 'ref' struct

相关代码:

迭代器方法

public IEnumerable<double> GridLevels()
{
    var step = (_upperLimit - _lowerLimit) / _gridCount;

    foreach (var i in Enumerable.Range(0, _gridCount))
    {
        var price = _lowerLimit + step * i;
        yield return price;
    }
}

自定义Range扩展

public static class RangeExtensions
{
    public static RangeEnumerator GetEnumerator(this Range range) => new(range);

    public ref struct RangeEnumerator
    {
        private readonly int _start;
        private readonly int _length;
        private int _count;
        public RangeEnumerator(Range range)
        {
            var (offset, length) = range.GetOffsetAndLength(int.MaxValue);
            (Current, _start, _length, _count) = (offset - 1, offset - 1, length, 0);
        }

        public bool MoveNext()
        {
            (var result, _count, Current) = (_count < _length, _count + 1, Current + 1);
            return result;
        }

        public void Reset() => (Current, _count) = (_start, 0);

        public int Current { get; private set; }

        public void Dispose() { }
    }
}

错误原因

自定义的RangeEnumerator是ref struct类型,而带yield return的迭代器方法会被编译器编译为状态机类,状态机需要存储枚举器实例。但ref struct的特性是只能存在于栈上,不能被装箱、也不能作为类的字段存储,因此编译器禁止在迭代器方法中使用这类枚举器。

解决方案

方案1:改用for循环(推荐)

直接用for循环替代foreach,既保持简洁性,又完全规避ref struct的限制,同时性能最优:

public IEnumerable<double> GridLevels()
{
    var step = (_upperLimit - _lowerLimit) / _gridCount;

    for (var i = 0; i < _gridCount; i++)
    {
        var price = _lowerLimit + step * i;
        yield return price;
    }
}

方案2:修改自定义枚举器为普通值类型

去掉RangeEnumerator的ref修饰符,将其改为普通struct。这样枚举器可以被状态机存储,但会失去ref struct的栈分配优势:

public static class RangeExtensions
{
    public static RangeEnumerator GetEnumerator(this Range range) => new(range);

    // 去掉ref关键字,改为普通struct
    public struct RangeEnumerator
    {
        private readonly int _start;
        private readonly int _length;
        private int _count;
        public RangeEnumerator(Range range)
        {
            var (offset, length) = range.GetOffsetAndLength(int.MaxValue);
            (Current, _start, _length, _count) = (offset - 1, offset - 1, length, 0);
        }

        public bool MoveNext()
        {
            (var result, _count, Current) = (_count < _length, _count + 1, Current + 1);
            return result;
        }

        public void Reset() => (Current, _count) = (_start, 0);

        public int Current { get; private set; }

        public void Dispose() { }
    }
}

方案3:放弃迭代器延迟加载(适合小数据量)

将Range遍历逻辑提前生成完整集合,再返回集合而非使用yield return,这样可以保留Range的foreach写法,但会一次性加载所有元素:

public IEnumerable<double> GridLevels()
{
    var step = (_upperLimit - _lowerLimit) / _gridCount;
    var levels = new List<double>(_gridCount);
    
    foreach (var i in 0.._gridCount)
    {
        levels.Add(_lowerLimit + step * i);
    }
    
    return levels;
}

内容的提问来源于stack exchange,提问作者nop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 19:35:18