在LINQ数组搜索中正确使用?.空条件操作符的疑问与解法
空条件操作符在foreach中失效的原因及优化方案
为什么你的?.写法无法正常工作?
问题出在两个核心点:
- 空条件操作符的作用范围有限:
Servers?.Find(...)仅在Servers为null时返回null,但后续的.Online没有加?.——如果Find返回null,直接访问.Online会触发空引用异常。就算给Online也加上?.,比如ServersHolder.Servers?.Find(f => f.ID == 9999)?.Online?.FindAll(...),最终如果某一步返回null,foreach尝试遍历null时还是会报错,因为foreach要求遍历对象必须是合法的枚举类型,不能为null。 - 链式调用的空值不自动传递:
?.只会对当前的成员访问生效,不会自动作用于整个调用链的后续环节。
先判断Exists的写法存在的问题
这种写法能避免空引用,但有明显缺陷:
- 重复遍历集合:
Exists和Find会先后遍历Servers集合查找目标服务器,造成不必要的性能损耗。 - 线程安全隐患:如果在
Exists检查和Find调用之间,Servers集合被修改(比如目标服务器被移除),Find会返回null,仍会触发空引用异常。
更优的解决方案
一次性获取目标服务器,逐层处理空值,并用空合并运算符确保foreach有合法的枚举对象:
var targetServer = ServersHolder.Servers?.Find(f => f.ID == 9999); // 用?? Enumerable.Empty<YourUserType>()确保即使前面返回null,也有可遍历的空集合 var targetUsers = targetServer?.Online?.FindAll(g => g.LoginPhase == 3) ?? Enumerable.Empty<YourUserType>(); foreach (var user in targetUsers) { await trySendPacket(9999, user.GUID.ToString(), OpCode.SentNotifyUserLeft, "", 2); }
如果使用C# 8.0及以上版本,还可以用模式匹配简化代码,可读性更高:
// 用模式匹配一次性验证非空,避免逐层判断 if (ServersHolder.Servers?.Find(f => f.ID == 9999) is { } targetServer && targetServer.Online?.FindAll(g => g.LoginPhase == 3) is { } targetUsers) { foreach (var user in targetUsers) { await trySendPacket(9999, user.GUID.ToString(), OpCode.SentNotifyUserLeft, "", 2); } }
这种写法既避免了重复遍历,又能安全处理所有空值场景,代码逻辑更严谨。
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

