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
相关产品推荐
相关产品推荐

