C#获取不重复的编码的最佳实践
作者:code bean
本文针对软件开发中“为新对象分配唯一编码”的常见需求,以C#通信设备管理场景为例,从原始代码分析入手,逐步讲解基于LINQ和哈希集合的优化方案,帮助开发者理解不同场景下的最佳实践,需要的朋友可以参考下
一、需求背景:为什么需要“不重复编码”?
在业务开发中,“编码唯一性”是保障数据准确性的基础要求。以通信设备管理系统为例:
- 系统维护一个通信模块集合
ComList
(存储ICommunication
类型对象) - 每个新接入的通信模块(
ec
)需分配唯一Encode
值 - 若
Encode
重复,会导致设备标识混乱,引发查询错误、指令发送失败等问题
因此,在将新模块添加到集合前,必须先找到一个“未被使用的编码”,这是确保系统稳定运行的关键步骤。
二、原始实现:逻辑可行但不够优雅
先看一段常见的原始代码,核心思路是“从0开始逐个检查,直到找到未使用的编码”:
// 原始代码:获取不重复编码 bool flag = false; int encode = 0; do { flag = true; // 假设当前编码可用 // 遍历集合检查编码是否已存在 foreach (ICommunication tempEC in ComList) { if (tempEC.Encode == encode) { encode++; // 编码已使用,自增后重新检查 flag = false; break; // 跳出foreach,进入下一轮do-while } } // 若编码可用,跳出循环 if (flag == true) { break; } } while (true); // 为新模块赋值并添加到集合 key = key + encode; ec.Key = key; ec.Encode = encode; ComList.Add(ec);
原始代码的问题
- 冗余标记变量:用
flag
控制循环退出,增加理解成本(实际可通过循环条件直接表达) - 嵌套层次深:
do-while
嵌套foreach
,再嵌套if
,代码结构复杂 - 重复遍历:每次检查都需手动遍历集合,未利用现有工具简化逻辑
三、第一次优化:用LINQ简化逻辑
C#的LINQ(Language Integrated Query) 提供了丰富的集合操作方法,其中Any()
方法可直接判断“集合是否存在满足条件的元素”,能大幅简化代码。
1. 优化后代码
// 优化方案1:基于LINQ的简洁实现 int encode = 0; // 循环条件:若集合中存在该编码,则继续自增检查 while (ComList.Any(ec => ec.Encode == encode)) { encode++; } // 后续赋值与添加逻辑不变 key = key + encode; ec.Key = key; ec.Encode = encode; ComList.Add(ec);
2. 核心逻辑解析:ComList.Any(ec => ec.Encode == encode)
这行代码是优化的核心,拆解理解:
ComList
:待检查的通信模块集合(存储ICommunication
对象)Any()
方法:LINQ扩展方法,作用是“判断集合是否至少存在一个满足条件的元素”- 返回值:
bool
(存在则true
,不存在则false
) - 优势:短路求值——找到第一个满足条件的元素后,立即停止遍历,避免无效循环
- 返回值:
ec => ec.Encode == encode
:lambda表达式(匿名函数),定义判断规则ec
:集合中元素的临时变量(可理解为“each communication”,建议取有意义的名称)ec.Encode
:获取当前模块的编码属性== encode
:判断当前编码是否与待分配的encode
重复
通俗解释:检查ComList
中是否有任何一个模块的Encode
等于当前encode
值。若有(返回true
),则encode
自增继续检查;若无(返回false
),则找到可用编码,循环结束。
3. 优化点总结
- 去除
flag
变量,用while
条件直接控制退出,逻辑更直观 - 消除嵌套层次,代码从“嵌套结构”变为“线性结构”,可读性提升
- 代码量减少60%+,同时保持功能完全一致
四、第二次优化:大数据量场景的性能提升
方案1在中小数据量(如ComList
元素<1000)场景下足够高效,但当集合元素极多(如万级以上)或需频繁分配编码时,每次调用ComList.Any()
都需遍历集合,性能会下降。
此时可通过哈希集合(HashSet) 优化——哈希集合的Contains
方法时间复杂度为O(1)
,远快于普通集合的O(n)
。
1. 优化思路
- 预提取
ComList
中所有已使用的Encode
,存入HashSet<int>
- 基于哈希集合的
Contains
方法检查编码是否重复,大幅减少查找时间
2. 优化后代码
// 优化方案2:大数据量场景下的高性能实现 // 步骤1:预提取已使用的编码到哈希集合 var existingEncodes = new HashSet<int>(ComList.Select(ec => ec.Encode)); // 步骤2:检查可用编码 int encode = 0; while (existingEncodes.Contains(encode)) { encode++; } // 后续赋值与添加逻辑不变 key = key + encode; ec.Key = key; ec.Encode = encode; ComList.Add(ec);
3. 关键代码解析
ComList.Select(ec => ec.Encode)
:LINQ的Select
方法“投影”集合元素,将ICommunication
对象转换为其Encode
属性(得到IEnumerable<int>
序列)new HashSet<int>(...)
:通过序列初始化哈希集合,存储所有已使用的编码existingEncodes.Contains(encode)
:哈希集合的快速查找方法,无论集合大小,均能瞬间判断编码是否存在
4. 适用场景
ComList
元素数量多(如>1000)- 短时间内频繁添加新模块(需多次获取不重复编码)
注意:若数据量小,方案2的“哈希集合初始化开销”可能大于遍历节省的时间,反而得不偿失,此时方案1更优。
五、总结:不同场景的方案选择
场景 | 推荐方案 | 核心优势 | 时间复杂度 |
---|---|---|---|
中小数据量(<1000) | LINQ Any() | 代码简洁、无额外开销 | O(n) |
大数据量/频繁操作 | 哈希集合 | 查找速度极快 | O(1) |
原始实现 | 不推荐 | 逻辑冗余、可读性差 | O(n²) |
最终推荐代码(通用场景)
若不确定数据量,可优先使用方案1(LINQ实现),代码简洁且满足多数业务需求:
// 完整通用实现代码 using System; using System.Collections.Generic; using System.Linq; // 需引用LINQ命名空间 // 假设的通信模块接口 public interface ICommunication { int Encode { get; set; } string Key { get; set; } } // 通信模块实现类 public class CommunicationModule : ICommunication { public int Encode { get; set; } public string Key { get; set; } } public class CommunicationManager { // 通信模块集合 private List<ICommunication> ComList = new List<ICommunication>(); // 添加新通信模块(核心方法) public void AddNewModule(string keyPrefix) { // 1. 获取不重复编码 int encode = 0; while (ComList.Any(ec => ec.Encode == encode)) { encode++; } // 2. 为新模块赋值 ICommunication ec = new CommunicationModule(); string key = keyPrefix + encode; ec.Key = key; ec.Encode = encode; // 3. 添加到集合 ComList.Add(ec); Console.WriteLine($"新模块添加成功:Key={key},Encode={encode}"); } }
六、拓展思考
- 编码起始值:若需从非0值(如100)开始分配编码,只需将
int encode = 0
改为int encode = 100
即可 - 编码步长:若需按固定步长(如2)分配编码,可将
encode++
改为encode += 2
- 并发安全:若多线程同时添加模块,需在编码检查和添加集合时加锁(如
lock(ComList)
),避免并发冲突
到此这篇关于C#获取不重复的编码的最佳实践的文章就介绍到这了,更多相关C#获取不重复编码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!