??用于提供默认值,is模式匹配用于类型安全的非空解包,二者分工明确、常配合使用:??短路求值且语义清晰,is则同时判空、解包并声明新变量。

直接说结论:用 ?? 处理默认值,用 is int number 这类模式匹配做类型安全的非空解包,两者不冲突、常配合使用。别再写 if (x != null) + 三元了。
什么时候该用 ?? 而不是三元运算符
?? 的核心价值是「短路求值」+「语义明确」:只在左操作数为 null 时才计算右操作数,且天然适配可空类型和引用类型。
- 右操作数如果是开销大的表达式(比如
GetDefaultConfig() ?? new Config()),??能避免无谓调用;三元写法x != null ? x : GetDefaultConfig()会强制执行右侧函数 -
??支持链式:a ?? b ?? c ?? throw new InvalidOperationException(),三元嵌套会迅速变得难读 - 对泛型方法也友好:
T GetValue<t>(T? value, T fallback) => value ?? fallback;</t>,这里??编译通过,而!= null在未约束泛型上会报错
is 模式匹配比 ?? 多解决什么问题
当你不仅想“取默认值”,还想“确认它有值并立刻用它的非空类型干活”时,is 模式匹配更精准。
-
??只返回值,不改变变量类型;if (maybeInt is int number)则同时完成判空 + 类型解包 + 新变量声明,后续可直接用number(int类型,非int?) - 对引用类型同样有效:
if (user is { Name: not null, Age: >= 18 })一行检查多个条件,??做不到 - 编译器保证
number只在if块内可见,杜绝误用未解包变量的隐患
??= 赋值陷阱:左侧必须是可赋值目标
??= 看似方便,但容易因左侧表达式非法而编译失败。
- 合法:
list ??= new List<string>();</string>(list是变量)、config.Options ??= new Options();(属性) - 非法:
GetList() ??= new List<string>();</string>(方法调用不能赋值)、(items?.FirstOrDefault()) ??= "default";(?.结果是右值,不可赋值) - 注意:即使左侧是
readonly字段或get-only 属性,??=也会报错——它要求左侧支持写入
混合使用的典型场景
真实代码里,?? 和 is 经常出现在同一逻辑流中,分工明确:
string? input = ReadInput();
// 先用 ?? 提供兜底,避免后续全链崩
string safeInput = input ?? string.Empty;
// 再用 is 做结构化验证,提取可信值
if (safeInput is { Length: > 0 } && int.TryParse(safeInput, out int parsed))
{
ProcessNumber(parsed); // parsed 是 int,不是 int?
}
else
{
LogError($"Invalid input: '{input}'");
}
最易被忽略的是:模式匹配中的 not null 不等于 ?? 的等价替换。比如 if (s is not null) 后续仍要用 s.Length,此时 s 类型仍是 string?;而 if (s is string valid) 才真正获得非空类型变量 valid。这个类型擦除时机,决定了你到底需要“默认值”还是“可信解包”。


















