C# 什么是空的!声明是什么意思?
我最近看到了以下代码:C# 什么是空的!声明是什么意思?,c#,c#-8.0,nullable-reference-types,C#,C# 8.0,Nullable Reference Types,我最近看到了以下代码: public class Person { //line 1 public string FirstName { get; } //line 2 public string LastName { get; } = null!; //assign null is possible public string? MiddleName { get; } = null; public Person(string fir
public class Person
{
//line 1
public string FirstName { get; }
//line 2
public string LastName { get; } = null!;
//assign null is possible
public string? MiddleName { get; } = null;
public Person(string firstName, string lastName, string middleName)
{
FirstName = firstName;
LastName = lastName;
MiddleName = middleName;
}
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
MiddleName = null;
}
}
基本上,我试图挖掘新的c#8特性。其中一个是NullableReferenceTypes
事实上,已经有很多关于它的文章和信息了。它相当好
但是我没有找到关于这个新语句的任何信息
null代码>
有人能给我解释一下吗
我为什么要用这个
而line1
和line2
之间有什么区别呢?这是理解null代码>意味着理解代码>运算符。
您以前可能将其用作“not”运算符。然而,由于C#8.0及其新特性,操作符得到了第二个含义。它可以用于类型来控制可空性,然后称为
典型用法
假设这一定义:
班级人员
{
//不是每个人都有中间名。我们将“无中间名”表示为“空”
公共字符串?中间名;
}
其用途是:
void LogPerson(个人)
{
Console.WriteLine(person.MiddleName.Length);//警告:可能为空
Console.WriteLine(person.MiddleName!.Length);//无警告
}
此运算符基本上会关闭此用法的编译器null检查
技术说明
零安全
C#8.0试图帮助您管理null
-值。默认情况下,它们不允许您将null
分配给所有内容,而是将内容翻转过来,现在要求您显式地标记所有您希望能够保留null
值的内容
这是一个非常有用的功能,它允许您通过强制做出决定并强制执行来避免NullReferenceException
s
工作原理
当谈到空安全性时,变量可以处于两种状态
- 可为null-可以为null
- 不可为空-不能为空
由于C#8.0,所有引用类型在默认情况下都不可为空。
自C#2.0以来,值类型一直不可为null
“可空性”可由2个新的(类型级别)运算符修改:
代码>=从可为空
到不可为空
?
=从不可空
到可空
这些运营商是相互对应的。
编译器使用这些运算符定义的信息来确保空安全性
例子
?
操作员用法。
此运算符告诉编译器变量可以包含空值
- 可空
字符串?x代码>
x
是引用类型-因此默认情况下不可为空
- 我们应用
?
运算符-这使它可以为null
x=null
工作正常
- 不可为空的
字符串y代码>
y
是引用类型-因此默认情况下不可为空
y=null
会生成警告,因为您将null值分配给不应该为null的对象
很高兴知道:使用string?
是System.Nullable的语法糖
代码>操作员用法。
此运算符告诉编译器可以为null的内容是可以安全访问的在这种情况下,您表示“不关心”空安全。
string x;
string? y;
x=y
- 非法
警告:“y”可能为空
- 赋值的左侧不可为null,但右侧可为null
- 所以它不起作用,因为它在语义上是不正确的
x=y代码>
- 合法强>
y
是一个应用了?
类型修饰符的引用类型,因此如果没有其他证明,它可以为null
- 我们应用
到y
,该覆盖其可空性设置,使其不可空
- 赋值的右侧和左侧不可为空。这在语义上是正确的
警告该
operator仅在类型系统级别关闭编译器检查-在运行时,该值可能仍然为null
小心使用!
您应该尝试避免使用空原谅运算符,因为会抵消编译器保证的空安全性的影响,因此使用空原谅运算符可能是系统中存在设计缺陷的征兆。
string x;
string? y;
推理
使用代码>运算符将创建很难找到的bug。
如果您有一个标记为不可为空的属性,您将假定您可以安全地使用它。但在运行时,您突然遇到NullReferenceException
并抓伤您的头。因为在使用绕过编译器检查后,值实际上变为null代码>
那么,为什么存在这个操作符呢?
在适当的使用情况下,存在有效的用例(在下面详细列出)。然而,在99%的情况下,您最好选择另一种解决方案。请不要打几十个代码>,只是为了使警告静音
- 在某些(边缘)情况下,编译器无法检测到可为null的值实际上是不可为null的
- 更容易的遗留代码库迁移
- 在某些情况下,您只是不在乎某些内容是否为空
- 使用单元测试时,您可能希望在出现
null
时检查代码的行为
好啊但是什么是null代码>平均值?
它告诉编译器null
不是可为null的值。听起来很奇怪,不是吗?
它与y相同代码>来自t
if (".js".Equals(Path.GetExtension(@"D:\file.js")))
{
var directory = Path.GetDirectoryName(includeFile);
var map = Path.Combine(
directory, // Compiler: Possible 'null' assignment to nun-nullable entity
"file.map");
}
if (".js".Equals(Path.GetExtension(@"D:\file.js")))
{
var directory = Path.GetDirectoryName(includeFile);
var map = Path.Combine(
directory!, // The warning is ignored because we are sure the directory variable is not null
"file.map");
}
public class Person
{
public Person(string name) => Name = name ?? throw new ArgumentNullException(nameof(name));
public string Name { get; }
}
[TestMethod, ExpectedException(typeof(ArgumentNullException))]
public void NullNameShouldThrowTest()
{
var person = new Person(null!);
}
public class MyDbContext : DbContext
{
public DbSet<Entity> Entities { get; set; } = null!; // Assigned by EF Core
public DbSet<Person> People { get; set; } = null!; // Assigned by EF Core
}
public class MyUnitTests
{
IDatabaseRepository _repo = null!;
[OneTimeSetUp]
public void PrepareTestDatabase()
{
...
_repo = ...
...
}
}