前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Nullable Reference Types 可空引用类型

Nullable Reference Types 可空引用类型

作者头像
solenovex
发布2019-10-15 16:41:39
8050
发布2019-10-15 16:41:39
举报
文章被收录于专栏:草根专栏

在写C#代码的时候,你可能经常会遇到这个错误:

但如果想避免NullReferenceException的发生,确实需要做很多麻烦的工作。

可空引用类型 Null Reference Type

所以,C# 8的可空引用类型就出现了。

C# 8可以让你在编译时就避免null引用错误,它并不是把null值从代码里移除了,而是它可以让你表达意图。具体的做法就是你可以告诉编译器哪个引用可能是null的,而哪些引用不可能是null。

看下面这个例子:

O references 
PrintPerson(Person person) 
public static void 
Console . WriteLine(person.Name);
O references PrintPerson(Person person) public static void Console . WriteLine(person.Name);

很显然,我们期待person这个参数它不可以是null的。

但是在C# 8之前,如果我们这样调用该方法,那么在编译时是不会报错的:

null; 
Person person 
PrintPerson(person);
null; Person person PrintPerson(person);

而如果运行程序,那么结果就是:

Console . WriteLine(person . Name); 
class Person 
•son = null 
Exception Unhandled 
System.NullReferenceException: 'Object reference not set to an 
LIC 
LIC 
string Name { 
{ get; 
int 
Age 
get; 
set; 
set; 
instance of an object.' 
person was null. 
View Details Copy Details 
Exception Settings 
Start Live Share session...
Console . WriteLine(person . Name); class Person •son = null Exception Unhandled System.NullReferenceException: 'Object reference not set to an LIC LIC string Name { { get; int Age get; set; set; instance of an object.' person was null. View Details Copy Details Exception Settings Start Live Share session...

打开null检查

而在Visual Studio 2019里面(C# 8的项目),修改项目文件,添加null检查之后:

'<Project Sdk-"Microsoft.NET.Sdl<"> 
<PropertyGroup> 
<OutputType>Exe</OutputType> 
<Target Framework>netcoreapp3. R/ Target Framework> 
PropertyGroup> 
<PropertyGroup> 
PropertyGroup> 
</Project>
'<Project Sdk-"Microsoft.NET.Sdl<"> <PropertyGroup> <OutputType>Exe</OutputType> <Target Framework>netcoreapp3. R/ Target Framework> PropertyGroup> <PropertyGroup> PropertyGroup> </Project>
null; , 
Person person 
PrintPerson(Eä2Q);
null; , Person person PrintPerson(Eä2Q);

这里就会出现警告。

例子:

有两个类,Person类的Address属性的类型是另外一个类:

2 references 
public class Person 
O references 
{ get; set; } 
public string 
Name 
I reference 
Address { get; set; 
public Address 
I reference 
public class Address 
I reference 
public string Province { get; set; 
O references 
{ get; set; } 
public string
2 references public class Person O references { get; set; } public string Name I reference Address { get; set; public Address I reference public class Address I reference public string Province { get; set; O references { get; set; } public string

现在可以看到,这些属性都出现了波浪线的警告,如果我们build一下这个项目,那么也会出现很多警告:

Show output from: Build 
' •n 'void printperson(person 
Rebuild All 
starte 
c: (30, 
1>Pro 
c: (31, 
1>Pro 
c: (24, 
1>Pro 
c: (25, 
1>Pro 
cs(ll, 
1>Pro 
cs(12, 
1>Pro 
n: 1 Appl — 
1>C0 
ne building 
Rebul 
23, 30, 31) 
23, 31, 27) 
23, 24, 27) 
24, 25, 31) 
29, 11, 33) 
25, 12, 31) 
warm 
warm 
warm 
warm 
warm 
warm 
d project: ConsoleÅppl, 
ng CS8618: 
ng CS8618: 
ng CS8618: 
ng CS8618: 
ng cs8600: 
ng CS8604: 
guy at 1 on: 
ble property ' 
ble proper 
ble proper 
ty 
ble proper 
ty 
Debug 
k-,y CPU 
ci ty' 
e 
Address' is 
s 'minitialized C n 
s 'minitializeå 
s 'minitializeå 
'minitialized C n 
for 
Consi der 
to non—null a 
1 ill 
r declaring the 
declaring the pr 
C n d declaring the pr 
r declaring the 
ble type 
property as null able 
operty as nullablæ 
operty as nullablæ 
property as null able 
Converting null lit al or possible null due 
\Cons01eÅppl 0\Conso e 
project "ConsoleÅppL c 
'Id All: 1
Show output from: Build ' •n 'void printperson(person Rebuild All starte c: (30, 1>Pro c: (31, 1>Pro c: (24, 1>Pro c: (25, 1>Pro cs(ll, 1>Pro cs(12, 1>Pro n: 1 Appl — 1>C0 ne building Rebul 23, 30, 31) 23, 31, 27) 23, 24, 27) 24, 25, 31) 29, 11, 33) 25, 12, 31) warm warm warm warm warm warm d project: ConsoleÅppl, ng CS8618: ng CS8618: ng CS8618: ng CS8618: ng cs8600: ng CS8604: guy at 1 on: ble property ' ble proper ble proper ty ble proper ty Debug k-,y CPU ci ty' e Address' is s 'minitialized C n s 'minitializeå s 'minitializeå 'minitialized C n for Consi der to non—null a 1 ill r declaring the declaring the pr C n d declaring the pr r declaring the ble type property as null able operty as nullablæ operty as nullablæ property as null able Converting null lit al or possible null due \Cons01eÅppl 0\Conso e project "ConsoleÅppL c 'Id All: 1

这是因为我们把这两个类的成员声明称了非null的引用类型,而我却没有对它们进行初始化。

成员可能是null的

如果我想让这些成员可以为null(意图上),那么就需要把它们设置为可null的(意图),在类型后边加上问号“?”即可:

2 references 
public class Person 
O references 
{ get; 
public string? 
Name 
I reference 
public Address? Address { 
I reference 
public class Address 
I reference 
public string? Province { 
O references 
City 
public string? 
get; 
set; } 
get; set; 
get; set; 
set;
2 references public class Person O references { get; public string? Name I reference public Address? Address { I reference public class Address I reference public string? Province { O references City public string? get; set; } get; set; get; set; set;

再次build项目之后,警告都没有了:

Show output from: Build 
Rebuild started: projec 
I >Cons01eÅppl — 
Rebuild All: 1 succeeded, 
ConsoleÅppl, 
gurati on: 
Debug kny CPU 
\Cons01eÅppl nso e 
0 1 ill
Show output from: Build Rebuild started: projec I >Cons01eÅppl — Rebuild All: 1 succeeded, ConsoleÅppl, gurati on: Debug kny CPU \Cons01eÅppl nso e 0 1 ill

然后再看一下这个方法:

O references 
Print(Person person) 
public static void 
Console . WriteLine(person.Name); 
Console . WriteLine( . Province) ;
O references Print(Person person) public static void Console . WriteLine(person.Name); Console . WriteLine( . Province) ;

这里person.Address.Province有一个警告,是因为Address可能是null。

可以有几种办法把这个警告去掉,首先是使用null条件操作符:

如果是Address是null的话,就输出null。

或者,如果你确认Address属性不会是null,那么可以在Address后添加一个叹号”!“,表示Address肯定不是null:

Console .WriteLine(person .Address ! . Province) ;
Console .WriteLine(person .Address ! . Province) ;

这个叹号的作用仅仅是去掉了警告,它没有改变任何运行时的状况,如果Address为null,那么这句话仍然会抛出NullReferenceException。

所以,只有确认我们访问的东西肯定不是null的时候,才应该使用"!"。

成员不可能是null

下面我更改一下思路意图,假设所有的成员都不可能为null,那么修改两个类:

类成员又出现了警告。

而回到方法里,我把叹号和问号都去掉之后,也不会出现警告了,因为它认为所有的成员都不会是null了:

但是还要记住,这个只是在编译时进行的检查,如果成员为null,还是会抛出异常的。这种操作对于运行时来说没有任何改变。

解决成员上出现的警告

使用构造函数对成员初始化,可以去掉这些警告:

另外一种办法就是直接对属性进行初始化:

我们还是采用构造函数初始化的办法吧。

往构造函数里传递null

那么往构造函数里面传递null会出现什么情况呢?试一下:

提示还是比较智能的,有警告,它说无法把null这个字面值转化为非null的引用类型。

另一种开启nullable检查的方式

如果把老项目的项目文件直接添加以下内容:

那么项目在编译的时候很可能出现大规模的问题。

所以一点一点启用nullable检查是比较好的做法。

首先我把项目文件恢复原状,然后打开某个文件,在文件最上面添加以下内容:

然后在文件的最下面添加:

这样的话,这个文件里面所有的内容都开起了nullable检查。

或者,我们也可以只针对一段代码进行检查:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-10-13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 可空引用类型 Null Reference Type
  • 打开null检查
  • 例子:
    • 成员可能是null的
      • 成员不可能是null
        • 往构造函数里传递null
        • 另一种开启nullable检查的方式
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档