了解null!
含义的关键是了解!
操作员。您之前可能已将其用作“非”运算符。但是从C#8.0开始,该运算符还可以用于控制Nullabilty的类型
!
在类型上使用什么运算符?
该!
运算符用于某种类型时,称为Null宽容运算符[ docs ]。它是在C#8.0中引入的
技术说明
典型用法
假设此定义:
class Person
{
public string? MiddleName;
}
用法是:
void LogPerson(Person person)
{
Console.WriteLine(person.MiddleName.Length);
Console.WriteLine(person.MiddleName!.Length);
}
此运算符基本上关闭编译器的空检查。
内部运作
使用此运算符可以告诉编译器可以安全地访问可能为空的内容。您表示打算在这种情况下“不关心”空安全性。
谈论null安全性时,变量可以处于2种状态。
从C#8.0开始,默认情况下所有引用类型都是不可为空的。
可以通过以下两个新的类型操作符来修改“可空性”:
!
=从Nullable
到Non-Nullable
?
=从Non-Nullable
到Nullable
这些运算符基本上是彼此对应的。编译器使用您使用这些运算符定义的信息来确保空安全性。
?
操作员用法。
可空 string? x;
x
是引用类型-因此默认情况下不可为null。
- 我们应用
?
运算符-这使其可为空。
x = null
工作正常。
不可为空 string y;
y
是引用类型-因此默认情况下不可为null。
y = null
由于将空值分配给不应为空的值,因此会生成警告。
!
操作员用法。
string x;
string? y = null;
x = y
- 非法!--
Warning: "y" may be null
- 分配的左侧为不可为空,而右侧则为可为空。
x = y!
- 法律!
- 分配的左侧和右侧不可为空。
- 自从
y!
将!
运算符应用于y
使其不可为空的运算符以来,一直有效。
警告的!
操作者仅关闭在一个类型的系统级的编译器检查-在运行时,仍然值可以为空。
这是一个反模式。
你应该尝试以避免使用!
空赦的运营商。
在此操作员适合使用的情况下,有有效的用例(在下面详细介绍),例如单元测试。不过,在99%的情况下,使用替代解决方案会更好。请不要!
在您的代码中拍打几十个,只是为了使警告静音。考虑一下您的原因是否真的值得使用。
使用-但要小心。如果没有具体目的/用例,请不要使用它。
它抵消了编译器保证的空安全性的影响。
使用!
运算符将很难发现错误。如果您有一个标记为不可为空的属性,则将假定您可以安全地使用它。但是在运行时,您突然遇到了一个问题NullReferenceException
并且抓了头。由于在绕过带有的编译器检查之后,值实际上变为null !
。
那么为什么这个运算符存在?
- 在某些情况下,编译器无法检测到可为空的值实际上是不可为空的。
- 遗留代码库迁移更容易。
- 在某些情况下,您根本不在乎某些东西是否为空。
- 使用单元测试时,您可能需要检查
null
通过时的代码行为。
具体回答您的问题。
那是什么null!
意思呢?
它告诉编译器这null
不是一个null
值。听起来很奇怪,不是吗?
与y!
上面的示例相同。因为您将运算符应用于null
文字,所以它看起来很奇怪。但是概念是相同的。
整理正在发生的事情。
public string LastName { get; } = null!;
该行定义了一个名为LastName
type的不可为空的类属性string
。由于它是不可为空的,因此从技术上讲,您不能为它分配null。
但是您只需要使用运算符就可以做到这一点-分配null
给LastName
- !
。因为null!
它不是null-就编译器所关心的null安全而言。
!
是可以为null的运算符,它告诉编译器,尽管通常它不允许这样做,但它应该以其他方式查找并允许它,因为我们知道得更多。null!
本身几乎没有实际用途,因为它全部否定了可空引用类型的用途。当您知道表达式不能为时null
,它会更有用,而编译器则不能。