如何简化从List<Info>提取非空UserId至string[]的LINQ代码?
更简洁高效的实现方案
当然有更优雅高效的写法啦!你的原代码需要两次遍历List<Info>集合,还得处理两个中间数组再合并,我们可以通过一次遍历完成所有操作,同时简化空值处理逻辑。
优化后的简洁实现
var userIds = Info .SelectMany(info => // 合并当前Info中Rhistory和Vhistory的有效UserId序列 (info.Rhistory?.Select(rh => rh.User?.UserId) ?? Enumerable.Empty<string>()) .Concat(info.Vhistory?.Select(vh => vh.User?.UserId) ?? Enumerable.Empty<string>()) ) .Where(id => !string.IsNullOrEmpty(id)) // 过滤空/空白ID .Distinct() // 去重 .ToArray();
代码解释
- 一次遍历集合:用
SelectMany直接遍历每个Info对象,同时展开它的Rhistory和Vhistory中的UserId,避免多次遍历原集合,提升性能(尤其是集合规模较大时)。 - 安全的空值处理:
- 使用
?.空条件运算符,避免Rhistory/Vhistory/User为null时引发空引用异常。 - 用
?? Enumerable.Empty<string>()确保当历史列表为null时返回空序列,不会中断链式调用。
- 使用
- 合并与过滤:通过
Concat合并两个历史列表的UserId序列,然后统一过滤空值、去重,最后转成数组。
进一步优化(可复用性)
如果后续还有类似的XXXhistory类需要提取UserId,可以把提取逻辑抽成复用方法,让代码更清晰:
// 通用的UserId提取方法 IEnumerable<string> ExtractUserIds<T>(IEnumerable<T> historyList) where T : class { return historyList?.Select(item => item.User?.UserId) ?? Enumerable.Empty<string>(); } // 调用方式 var userIds = Info .SelectMany(info => ExtractUserIds(info.Rhistory).Concat(ExtractUserIds(info.Vhistory))) .Where(id => !string.IsNullOrEmpty(id)) .Distinct() .ToArray();
这样不仅代码更整洁,后续新增类似的历史类型时,直接复用ExtractUserIds方法即可,扩展性更强。
内容的提问来源于stack exchange,提问作者Divya Agrawal
相关产品推荐
相关产品推荐

