LINQ扁平化循环引用:优雅实现用户主管层级结构构建
优雅实现用户主管层级全链路(LINQ版)
嘿,完全懂你的痛点!用嵌套for循环一层层往上爬确实越写越乱,尤其是层级多的时候,维护起来头疼。咱们用LINQ结合迭代器或者递归的方式,能写出简洁又易读的代码,完美解决这个问题。
首先先修正下用户类的C#语法(原写法有点小问题):
public class User { public int UserId { get; set; } public string Name { get; set; } public int SupervisorId { get; set; } }
方法一:递归式LINQ(简洁直观)
这种方式用C#的迭代器(yield关键字)配合递归,代码非常干净,适合层级不是特别深的场景:
public static IEnumerable<User> GetFullSupervisorChain(User startingUser, IEnumerable<User> allUsers) { if (startingUser == null) yield break; // 先返回当前用户 yield return startingUser; // 查找当前用户的直接主管 var supervisor = allUsers.FirstOrDefault(u => u.UserId == startingUser.SupervisorId); // 终止条件:主管不存在,或者主管是自己(避免循环引用) if (supervisor == null || supervisor.UserId == startingUser.UserId) yield break; // 递归获取主管的层级链,拼接到当前结果中 foreach (var user in GetFullSupervisorChain(supervisor, allUsers)) { yield return user; } }
怎么用?
假设你有所有用户的集合allUsers,以及起始用户currentUser,直接调用即可:
var hierarchy = GetFullSupervisorChain(currentUser, allUsers).ToList(); // 如果想从最高负责人到当前用户反转,加个Reverse() var reversedHierarchy = hierarchy.Reverse().ToList();
方法二:迭代式LINQ(避免栈溢出)
如果你的用户层级特别深(比如几十上百层),递归可能会触发栈溢出。这时候用迭代式的LINQ实现更安全,同时效率也更高(提前把用户转成字典,查找主管是O(1)时间):
public static IEnumerable<User> GetFullSupervisorChainIterative(User startingUser, IEnumerable<User> allUsers) { if (startingUser == null) yield break; // 先把用户转成字典,大幅提升查找效率 var userLookup = allUsers.ToDictionary(u => u.UserId); var currentUser = startingUser; // 先返回起始用户 yield return currentUser; // 循环查找主管,直到找不到或者出现循环 while (userLookup.TryGetValue(currentUser.SupervisorId, out var supervisor) && supervisor.UserId != currentUser.UserId) { yield return supervisor; currentUser = supervisor; } }
哦对了,这里用yield配合while循环,本质上也是LINQ友好的实现,而且完全避免了递归栈的问题。
额外小贴士
- 如果你系统里最高负责人的
SupervisorId是固定值(比如0),可以把终止条件改成supervisorId == 0,这样更贴合业务场景。 - 记得处理循环引用的情况(比如A的主管是B,B的主管是A),不然会无限循环,上面的代码都加了
supervisor.UserId != currentUser.UserId的判断来避免这个问题。
内容的提问来源于stack exchange,提问作者TrevorGoodchild
相关产品推荐
相关产品推荐

