如何在函数中使用任意类型,实现递归引用循环检查方法的通用化
你可以通过泛型参数+抽离类型耦合逻辑的方式实现通用的循环引用检查方法,以下是两种可直接落地的实现方案:
方案1:委托适配方案(无类型侵入,适配所有层级结构类型)
把和具体类型绑定的唯一标识判断、子节点加载、子节点获取逻辑抽为委托参数,不需要修改原有类的定义,适配性最高:
/// <summary> /// 通用层级结构循环引用检查方法 /// </summary> /// <typeparam name="T">待检查的层级节点类型</typeparam> /// <param name="currentObj">当前检查的节点对象</param> /// <param name="visitStack">已访问节点栈</param> /// <param name="existCheck">判断当前节点是否已存在于访问栈的逻辑</param> /// <param name="loadChildItems">加载当前节点子节点的逻辑</param> /// <param name="getChildItems">获取当前节点子节点集合的逻辑</param> /// <returns>是否存在循环引用</returns> private bool CheckReferenceLoop<T>(T currentObj, List<T> visitStack, Func<T, bool> existCheck, Action<T> loadChildItems, Func<T, IEnumerable<T>> getChildItems) { bool haveLoop = false; if (existCheck(currentObj)) { haveLoop = true; } else { visitStack.Add(currentObj); loadChildItems(currentObj); foreach (var child in getChildItems(currentObj)) { if (!haveLoop) { haveLoop = CheckReferenceLoop(child, visitStack, existCheck, loadChildItems, getChildItems); } } // 可选:如果需要复用visitStack实例,可添加回溯逻辑 // visitStack.RemoveAt(visitStack.Count - 1); } return haveLoop; }
对应你原有Organization场景的调用示例:
Data.DataContext db = new Data.DataContext(); Organization targetOrg = 获取待检查的根节点(); var visitStack = new List<Organization>(); bool hasLoop = CheckReferenceLoop( currentObj: targetOrg, visitStack: visitStack, existCheck: obj => visitStack.Find(a => a.Id == obj.Id) != null, loadChildItems: obj => obj.GetChildItems(db), getChildItems: obj => obj.ChildItems );
方案2:接口约束方案(适合多类统一实现层级结构的场景)
如果项目中大量需要做循环检查的层级类可以统一实现公共接口,可通过泛型约束简化调用逻辑,减少重复传参:
首先定义统一的层级节点接口:
public interface IHierarchyNode<out T> { // 节点唯一标识,可根据实际场景调整类型为Guid/string等 int Id { get; } // 子节点集合 IEnumerable<T> ChildItems { get; } // 加载子节点的方法,可根据实际场景调整入参 void GetChildItems(Data.DataContext db); }
让你的Organization类实现上述接口后,可直接使用约束后的泛型方法:
private bool CheckReferenceLoop<T>(T currentObj, List<T> visitStack, Data.DataContext db) where T : IHierarchyNode<T> { bool haveLoop = false; if (visitStack.Find(a => a.Id == currentObj.Id) != null) { haveLoop = true; } else { visitStack.Add(currentObj); currentObj.GetChildItems(db); foreach (var child in currentObj.ChildItems) { if (!haveLoop) { haveLoop = CheckReferenceLoop(child, visitStack, db); } } } return haveLoop; }
调用时直接传入参数即可,不需要额外传委托:
var visitStack = new List<Organization>(); bool hasLoop = CheckReferenceLoop(targetOrg, visitStack, db);
可选性能优化
如果需要检查的层级结构节点数量较多,可将访问栈从List<T>替换为HashSet<int>(存储节点Id),将存在性判断的时间复杂度从O(n)降低到O(1),大幅提升检查效率。
内容的提问来源于stack exchange,提问作者Acces969
相关产品推荐
相关产品推荐

