.NET 6+ 应直接使用 ArgumentNullException.ThrowIfNull(input),自动推导参数名、语义清晰、性能更优;旧版本需显式判断,避免自定义扩展方法;?? throw 适用于赋值场景,不替代 ThrowIfNull;Span 等 ref struct 类型不可为空,须用 IsEmpty 检查。

ArgumentNullException 怎么抛才不啰嗦
直接用 ArgumentNullException.ThrowIfNull,.NET 6+ 原生支持,一行解决,不用手写 if (x == null) throw new ArgumentNullException(nameof(x))。
这个方法是静态的、泛型推导友好的,且会自动填充参数名(nameof(x) 不用手写):
void Process(string input)
{
ArgumentNullException.ThrowIfNull(input); // ✅ 自动把 "input" 当作 paramName
// 后续逻辑
}它不返回值,也不做任何其他事——就是纯判空 + 抛异常,语义干净,性能也比手动判空略优(JIT 可能内联)。
旧项目还在用 .NET 5 或更早?别硬升
低版本没有 ThrowIfNull,但别自己封装一个“看起来一样”的扩展方法——容易踩两个坑:
- 扩展方法无法参与 JIT 内联,多一层调用开销(虽小但存在)
- 如果写成
public static void ThrowIfNull(this object? value, string? paramName = null),paramName默认为null时,ArgumentNullException构造函数会抛NullReferenceException(不是你想要的)
稳妥做法仍是显式判断:
if (input == null)
throw new ArgumentNullException(nameof(input));或者升级到 .NET 6+,这是最省心的长期解。
为什么不用 C# 11 的 ?? throw 代替?
?? throw 是表达式,适用于赋值或返回场景,但和 ThrowIfNull 职责不同:
-
var x = input ?? throw new ArgumentNullException(nameof(input));—— 你要的是“取值”,顺便判空 -
ArgumentNullException.ThrowIfNull(input);—— 你要的是“确保非空”,不产生新变量,也不影响控制流结构
混用会导致语义混乱。比如在 void 方法开头写 input ?? throw ...,编译器会警告“表达式值未被使用”,还得套个括号或丢给 _,反而更丑。
容易被忽略的细节:数组和 Span<T> 判空要小心
ThrowIfNull 对引用类型安全,但对 T[]、Span<t></t>、ReadOnlySpan<t></t> 这类“看似像引用,实则可空”的类型,要注意:
-
string[] arr = null;→ThrowIfNull(arr)正确生效 -
Span<int> span = default;</int>→ThrowIfNull(span)不会抛异常!因为Span<t></t>是ref struct,不能为null,它的默认值是合法空 span
对 Span 类型,该用 span.IsEmpty 显式检查,而不是依赖 ThrowIfNull。


















