一元后缀 ! 运算符是 null 包容运算符或 null 抑制运算符。 在已启用 可为 null 的批注上下文中,使用 null 表示运算符来抑制上述表达式的所有可为 null 警告。 一元前缀 ! 运算符是逻辑非运算符。 null 包容运算符在运行时不起作用。 它仅通过更改表达式的 null 状态来影响编译器的静态流分析。 在运行时,表达式 x! 的计算结果为基础表达式 x 的结果。
有关可为空引用类型特性的详细信息,请参见可为空引用类型。
C# 语言参考记录了 C# 语言的最新发布版本。 它还包含即将发布的语言版本公共预览版中功能的初始文档。
本文档标识了在语言的最后三个版本或当前公共预览版中首次引入的任何功能。
小窍门
若要查找 C# 中首次引入功能时,请参阅 有关 C# 语言版本历史记录的文章。
示例
null 表示运算符的一个用例是测试参数验证逻辑。 例如,请考虑以下类:
#nullable enable
public class Person
{
public Person(string name) => Name = name ?? throw new ArgumentNullException(nameof(name));
public string Name { get; }
}
通过使用 MSTest 测试框架,可以为构造函数中的验证逻辑创建以下测试:
[TestMethod, ExpectedException(typeof(ArgumentNullException))]
public void NullNameShouldThrowTest()
{
var person = new Person(null!);
}
如果不使用 null 包容运算符,编译器将为前面的代码生成以下警告:Warning CS8625: Cannot convert null literal to non-nullable reference type。 通过使用 null 放弃运算符,可以通知编译器 null 传递是预期的,不应生成警告。
如果确定表达式不能 null ,但编译器无法识别该表达式,则也可以使用 null 放弃运算符。 在下面的示例中,如果 IsValid 方法返回 true,则其参数不是 null,可以放心取消对它的引用:
public static void Main()
{
Person? p = Find("John");
if (IsValid(p))
{
Console.WriteLine($"Found {p!.Name}");
}
}
public static bool IsValid(Person? person)
=> person is not null && person.Name is not null;
如果没有 null 包容运算符,编译器将为 p.Name 代码生成以下警告:Warning CS8602: Dereference of a possibly null reference。
如果可以修改 IsValid 方法,则可使用 NotNullWhen 属性告知编译器,当方法返回 IsValid 时,null 方法的参数不能是 true:
public static void Main()
{
Person? p = Find("John");
if (IsValid(p))
{
Console.WriteLine($"Found {p.Name}");
}
}
public static bool IsValid([NotNullWhen(true)] Person? person)
=> person is not null && person.Name is not null;
在前面的例子中,不需要使用 null 包容运算符,因为编译器有足够的信息来发现 p 不能是 null 语句中的 if。 如需深入了解允许你提供有关变量 null 状态的其他信息的属性,请参阅使用属性升级 API 以定义 null 期望值。
C# 语言规范
有关详细信息,请参阅可为空的引用类型规范草案的 null 包容性运算符部分。