C#最佳实践:如何在循环调用的Action<>回调中实现break?
自定义ForEach循环提前终止的最佳实践
针对你实现的ForEachEdge方法,要支持回调中提前终止循环,下面分析各方案并给出最优实践:
方案1:回调返回状态值(推荐)
将回调类型从Action<Edge>改为Func<Edge, bool>,让回调返回布尔值表示是否继续遍历——返回true继续,false终止循环。这种设计和.NET原生API(如Enumerable.TakeWhile、List.Exists)的语义一致,符合终端开发者的直觉。
修改后的方法实现
public void ForEachEdge(in Vertex vertex, Func<Edge, bool> callback) { var edge = GetEdge(vertex.BaseEdgeIndex); do { // 回调返回false时直接终止循环 if (!callback.Invoke(edge)) break; edge = GetEdge(edge.GetNext(vertex.Index)); } while (edge.Index != vertex.BaseEdgeIndex); }
调用示例
ForEachEdge(edge.Vertex0Index, (e) => { if (inEdge.AreConnectingSameVertices(e)) { // 返回false跳出循环 return false; } // 执行正常遍历逻辑 return true; });
方案2:传入ref bool参数
通过ref bool stopEnumerating参数让回调修改外部变量的状态,循环中每次检查该变量决定是否终止。这种方式可行,但需要终端开发者额外维护一个外部变量,容易出现初始化遗漏或状态污染的问题,可读性和简洁性不如返回值方案。
示例代码
public void ForEachEdge(in Vertex vertex, Action<Edge, ref bool> callback) { bool stop = false; var edge = GetEdge(vertex.BaseEdgeIndex); do { callback.Invoke(edge, ref stop); if (stop) break; edge = GetEdge(edge.GetNext(vertex.Index)); } while (edge.Index != vertex.BaseEdgeIndex); } // 调用 bool stop = false; ForEachEdge(edge.Vertex0Index, (e, ref s) => { if (inEdge.AreConnectingSameVertices(e)) { s = true; } });
不推荐的方案
- 抛出异常:用异常控制正常流程是反模式,会严重影响性能,且不符合异常的语义(异常应仅用于错误场景)。
- 包装类传递状态:用自定义类包装bool值的方式,本质和
ref参数类似,但会增加不必要的代码复杂度,完全没必要。
总结
优先选择回调返回状态值的方案,它语义清晰、代码简洁,完全贴合.NET开发者对遍历API的使用预期,是最符合终端开发者直觉的实现方式。
内容的提问来源于stack exchange,提问作者CodeSmile
相关产品推荐
相关产品推荐

