TreeView控件基于现有节点最大值获取下一个可用节点名称的实现问题
问题排查
原代码存在以下4个核心问题,导致功能不符合预期且性能不达标:
- 首分支逻辑反转:
if (NodeExistsInSection(treeView, searchFor))分支直接返回输入名称,和需求完全相反——需求是节点不存在时直接返回输入名,存在时才需要生成带后缀的新名称。 - 递归遍历漏判节点:
LoopNodesRecursive仅校验传入节点的子节点文本,未校验节点本身的文本,导致根节点、父层级节点的匹配结果全部被遗漏。 - 名称生成逻辑低效:采用「后缀累加+逐次全树搜索」的实现方式,后缀数值越大,全树遍历次数越多,若当前最大后缀为500,就需要触发500次全树遍历,性能消耗随后缀数值线性增长。
- 重复遍历逻辑冗余:
NodeExistsInSection和SearchRecByText功能重合度极高,无谓增加了额外的全树遍历开销。
优化方案
核心优化思路为单次全树遍历提取所有匹配前缀的节点后缀,直接计算最大后缀加1,仅需1次全树遍历即可完成全部计算,时间复杂度稳定为O(n),不受后缀数值大小影响,完美适配节点数大于500的场景。
具体规则兼容:
- 普通前缀场景:匹配到
test、test1~test5时,直接取最大后缀5+1生成test6 - 纯数字场景:前缀为空时,匹配到
20~30,取最大后缀30+1生成31
修正后完整代码
internal static string GetNextAvailableName(TreeView treeView, string inputName) { // 第一步:拆分输入名称的前缀和初始后缀 string prefix = inputName; int initSuffix = 0; var match = Regex.Match(inputName, @"\d+$", RegexOptions.RightToLeft); if (match.Success) { initSuffix = int.Parse(match.Value); prefix = inputName.Substring(0, inputName.Length - match.Value.Length); } // 第二步:单次遍历所有节点,收集所有符合前缀的后缀数值 int maxSuffix = initSuffix - 1; foreach (TreeNode rootNode in treeView.Nodes) { TraverseAllNodes(rootNode, node => { if (node.Text.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { // 提取后缀数字 var suffixPart = node.Text.Substring(prefix.Length); if (int.TryParse(suffixPart, out int suffix)) { if (suffix > maxSuffix) maxSuffix = suffix; } // 处理无前缀的原始名称(比如test,对应后缀为0) else if (string.IsNullOrEmpty(suffixPart) && 0 > maxSuffix) { maxSuffix = 0; } } }); } // 第三步:生成最终名称 if (maxSuffix < 0) return inputName; return maxSuffix == 0 ? $"{prefix}" : $"{prefix}{maxSuffix + 1}"; } // 递归遍历所有节点的通用方法 private static void TraverseAllNodes(TreeNode currentNode, Action<TreeNode> processNode) { processNode(currentNode); foreach (TreeNode childNode in currentNode.Nodes) { TraverseAllNodes(childNode, processNode); } }
内容的提问来源于stack exchange,提问作者jms2505
相关产品推荐
相关产品推荐

