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

C#实现迭代器模式:验证失败时提前返回空枚举的问题

问题与解决方案

问题背景

原有两段遍历物料列表的逻辑:

  • 第一段验证每个物料,失败则直接退出,同时收集验证通过的物料
  • 第二段仅在第一段成功收集到有效物料后,对这些物料执行操作

希望通过迭代器模式合并逻辑,实现仅当所有物料验证通过时才执行遍历操作,但当前实现中,验证失败时无法正确返回空枚举。

原逻辑代码:

foreach (var material in matrix.Materials)
{
    var validationResult = materialValidator.Validate(material);
    if (validationResult.IsValid == false)
    {
        return;
    }

    validMaterials.Add(
        new Tuple<IMaterial, string>(
            materialValidator.CurrentMaterial,
            material.TextMask));
}

foreach (var tuple in validMaterials)
{
    var material = tuple.Item1;
    var text = tuple.Item2;

    material.SetSelcted(true);

    if (string.IsNullOrWhiteSpace(text) == false)
    {
        module.SetTextMask(textMask);
    }
}

期望的迭代器使用方式:

var enumerable = new MaterialsEnumerable(matrix.Materials);
foreach (var iterator in enumerable)
{
    iterator.Material.SetSelcted(true);

    if (string.IsNullOrWhiteSpace(iterator.Text) == false)
    {
        module.SetTextMask(iterator.Text);
    }
}

当前实现的问题点:
在使用yield的迭代器方法中,直接返回Enumerable.Empty<MaterialIterator>()是无效的——因为yield生成的是状态机,return语句会直接终止迭代,但已经yield return出去的元素仍然会被消费者接收到,无法达到“返回空枚举”的效果。

解决方案

核心思路:先完成所有物料的验证,确认全部通过后再返回枚举;若有任何一个验证失败,直接返回空枚举。

方案1:提前验证并缓存结果

在类的构造阶段完成所有验证,缓存有效物料或标记验证失败状态,之后在迭代器中根据状态返回结果:

public class MaterialsEnumerable : IEnumerable<MaterialIterator>
{
    private readonly IEnumerable<IMaterial> _materials;
    private readonly IMaterialValidator _validator;
    private List<MaterialIterator> _validItems;
    private bool _validationFailed;

    public MaterialsEnumerable(IEnumerable<IMaterial> materials, IMaterialValidator validator)
    {
        _materials = materials;
        _validator = validator;
        ValidateAllMaterials();
    }

    private void ValidateAllMaterials()
    {
        _validItems = new List<MaterialIterator>();
        foreach (var material in _materials)
        {
            var result = _validator.Validate(material);
            if (!result.IsValid)
            {
                _validationFailed = true;
                _validItems.Clear();
                return;
            }
            _validItems.Add(new MaterialIterator(_validator.CurrentMaterial, material.TextMask));
        }
    }

    public IEnumerator<MaterialIterator> GetEnumerator()
    {
        if (_validationFailed)
        {
            yield break;
        }
        foreach (var item in _validItems)
        {
            yield return item;
        }
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

方案2:延迟验证(迭代器内完成全量验证)

如果需要惰性执行验证逻辑,可以在迭代器方法中先遍历所有物料完成验证,收集有效项后再决定是否返回:

public IEnumerator<MaterialIterator> GetEnumerator()
{
    var validItems = new List<MaterialIterator>();
    bool hasFailed = false;

    foreach (var material in this.materials)
    {
        var result = this.validator.Validate(material);
        if (!result.IsValid)
        {
            hasFailed = true;
            break;
        }
        validItems.Add(new MaterialIterator(this.validator.CurrentMaterial, material.TextMask));
    }

    if (!hasFailed)
    {
        foreach (var item in validItems)
        {
            yield return item;
        }
    }
}

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

说明

  • 两种方案都确保了只有当所有物料验证通过时,才会返回可遍历的有效项;一旦有验证失败,消费者的foreach循环不会执行任何迭代逻辑,等同于遍历空枚举。
  • 方案1提前验证,适合物料列表不会动态变化的场景;方案2延迟验证,适合需要惰性执行的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 14:07:35