?. 运算符是带短路的成员访问,用于明确接受链式调用中途为 null 并自然返回 null;适用如 person?.address?.city?.name,不适用如 order?.status 直接传非可空参数。

?. 运算符不是“更安全的 .”,而是“带短路的成员访问”——它不阻止 null,而是让 null 传播得更干净。用错地方反而掩盖逻辑缺陷。
什么时候该用 ?.,而不是 if 或 ??
核心判断标准:你是否**明确接受链式调用中途为 null 的结果,并希望整个表达式自然返回 null(或可空类型)**。
- 适合:
person?.Address?.City?.Name—— 没有 person、没地址、没城市,都不该报错,结果就是null - 不适合:
order?.Status后直接传给UpdateStatus(status)(方法参数是string,非可空)——编译不过,且语义上 status 不该为 null - 别混用:
list?.Count() ?? 0是合理组合;但list?.Count() + 1会编译失败,因为int?不能直接加int
?.ToString() 看似安全,实则可能返回 null 而非空字符串
这是新手高频误判点:以为调用了 ToString() 就一定有字符串,其实只要左侧是 null,整条链就停在 ?.ToString() 并返回 null。
-
StringBuilder sb = null; string s = sb?.ToString();→s是null,不是"" - 若需要空字符串兜底,得接
?? "":sb?.ToString() ?? "" - 若对象本身重写了
ToString()且可能返回null(少见但合法),?.ToString()仍会原样返回那个null,不额外拦截
索引器 ?[i] 和数组/集合为空时的行为差异
?[i] 只检查左侧对象是否为 null,**不检查索引越界或集合为空**。
-
string s = null; char? c = s?[0];→ 安全,c是null -
string s = ""; char? c = s?[0];→ 抛IndexOutOfRangeException!因为s非 null,?[0]等价于s[0] - 对
List<t></t>同理:list?[0]不防list.Count == 0,只防list == null - 真要防越界,得用
list?.Count > i ? list[i] : default(T)或封装辅助方法
链式调用后类型自动变为可空,后续运算必须适配
这是编译器强制的类型规则,不是风格建议。一旦用了 ?.x,只要 x 是值类型,结果必为对应可空类型(如 DateTime?.CreatedTime → DateTime?)。
-
int? count = items?.Count();正确;int count = items?.Count();编译错误 -
bool isActive = user?.IsActive ?? false;正确;bool isActive = user?.IsActive;编译错误(bool?不能隐式转bool) - 调用方法返回
void时,obj?.DoSomething()合法,但结果无意义,仅用于避免 NRE
最易被忽略的是:null 条件运算符解决的是“访问链断裂”的问题,不是“业务逻辑缺省”的问题。该用 ?? 做默认值的地方,硬套 ?.?? 可能掩盖本该校验的数据完整性缺陷。










