在C#属性中执行集合排序并设置排名是否合理?有无更优实现方案?
现有实现存在的问题
- 性能损耗严重:每次访问属性都会触发全集合排序,同时遍历中调用
IndexOf查找每个元素的索引,时间复杂度达到O(n²),集合元素量大时性能问题会非常明显。 - 存在非预期副作用:直接修改原始
Score对象的排名属性,若其他代码逻辑也引用了相同的Score实例,会意外拿到被修改的排名值,多线程访问时还会出现数据竞争问题。 - 不符合属性设计规范:属性的get访问器默认应是轻量、无副作用的操作,当前实现包含重计算逻辑且会修改原始数据,容易给调用方造成误解。
- 同分排名逻辑不合理:默认相同分数的元素会按照在列表中的顺序分配不同排名,如果业务需要同分同排名,现有逻辑无法满足。
优化方案
方案1:最小改动修复现有问题(适合访问频率低、集合量小的场景)
首先优化排序和索引计算逻辑,去掉低效的IndexOf调用,同时将重计算逻辑改为方法,明确告知调用方该操作有计算成本:
private readonly ICollection<Score> _winningTable = new List<Score>(); public List<Score> GetWinningTable() { var orderedList = _winningTable.OrderByDescending(w => w.Score).ToList(); // 用索引循环直接获取位置,避免每次查找索引 for (int i = 0; i < orderedList.Count; i++) { orderedList[i].EditScore((decimal)orderedList[i].Score, i + 1); } return orderedList; }
方案2:无副作用实现(推荐,不修改原始Score对象)
如果不希望修改原始Score实例,建议返回DTO存储排名,避免影响其他引用该实例的逻辑:
// 定义带排名的DTO public class RankedScore { public Score ScoreInfo { get; set; } public int Rank { get; set; } } public List<RankedScore> GetWinningTable() { return _winningTable .OrderByDescending(w => w.Score) .Select((score, index) => new RankedScore { ScoreInfo = score, Rank = index + 1 }) .ToList(); }
方案3:写入时更新缓存(适合读多写少的场景)
如果榜单更新频率低、访问频率高,建议在集合新增/删除/修改元素时统一计算排名并缓存,读取时直接返回缓存结果,避免每次访问都重复计算:
private readonly List<Score> _winningTable = new List<Score>(); private List<RankedScore> _cachedRankedTable; private bool _isCacheDirty = true; // 标记数据是否变更,需要重算缓存 // 新增元素示例 public void AddScore(Score score) { _winningTable.Add(score); _isCacheDirty = true; } // 删除元素示例 public void RemoveScore(Score score) { if (_winningTable.Remove(score)) { _isCacheDirty = true; } } public List<RankedScore> GetWinningTable() { if (_isCacheDirty) { _cachedRankedTable = _winningTable .OrderByDescending(w => w.Score) .Select((score, index) => new RankedScore { ScoreInfo = score, Rank = index + 1 }) .ToList(); _isCacheDirty = false; } // 返回副本避免外部修改缓存数据 return new List<RankedScore>(_cachedRankedTable); }
同分同排名实现扩展
如果业务需要相同分数的元素排名一致,可调整排名计算逻辑:
public List<RankedScore> GetWinningTableWithSameRank() { var orderedList = _winningTable.OrderByDescending(w => w.Score).ToList(); int rank = 1; var result = new List<RankedScore>(); for (int i = 0; i < orderedList.Count; i++) { if (i > 0 && orderedList[i].Score != orderedList[i-1].Score) { rank = i + 1; } result.Add(new RankedScore { ScoreInfo = orderedList[i], Rank = rank }); } return result; }
内容的提问来源于stack exchange,提问作者Juanma
相关产品推荐
相关产品推荐

