我们知道,在C#中,数据类型分为值类型和引用类型,值类型的变量不可以为空,而引用类型的变量可以为空。
但是,在使用引用类型的时候,如果不注意检查引用类型变量是否可为空,程序中就有困难出现NullReferenceException异常。
C#8.0中提供了”可为空的引用类型”语法,可以在引用类型后添加”?”修饰符声明这个类型是可为空的。
对于没有添加”?”修饰符的引用类型的变量,当编译器发现存在为这个变量赋值null的可能性的时候,编译器会给出警告信息。
在Visual Studio 2022 中,这个特性是默认启用的,可以通过删除项目*.csproj文件中的<Nullable>disable</Nullable>关闭这个特性。
如下代码所示,编写一个包含Name、PhoneNumber两个属性的Student类。
public class Student
{
public string Name { get; set; }
public string PhoneNumber { get; set; }
public Student(string name)
{
this.Name = name;
}
}
上面的代码在编译的时候,编译器会给出”在退出构造方法时,不可为null的属性PhoneNumber必须包含非null值”这样的警告信息。
Name、PhoneNumber两个属性都是string类型,因此它们都是”不可为空的string类型”,但是Student类的构造方法中只为Name属性赋值了,这样就存在PhoneNumber属性没有被赋值,从而导致其属性值为空的可能性,因此编译器给出了这样的警告信息。
如果想消除这个警告信息,可以将构造方法声明为Student(string name,string phoneNumber),并为两个属性都赋值,但是如果PhoneNumber属性确实可以为空,就可以把PhoneNumber属性声明为string?类型,也就是允许为空的string类型。
如以下代码所示:
public class Student
{
public string Name { get; set; }
public string? PhoneNumber { get; set; }
public Student(string name)
{
this.Name = name;
}
}
由于上面定义的Student类的PhoneNumber属性可能为空,因此下面的代码中的第五行执行后会出现”解引用可能出现空引用”这样的警告信息。
Student student = new(string.Empty);
Console.WriteLine(student.Name.ToLower());
Console.WriteLine(student.PhoneNumber.ToLower();
可以用非空检查来对PhoneNumber属性来避免这个警告
Student student = new(string.Empty);
Console.WriteLine(student.Name.ToLower());
if (student.PhoneNumber != null)
{
Console.WriteLine(student.PhoneNumber.ToLower());
}
else
{
Console.WriteLine("手机号为空");
}
当然,如果确认被访问的变量、成员不会出现为空的情况,也可以在访问可为空的变量、成员的时候加上!来抑制编译器的警告。
当然,要尽量避免使用!印制警告。
Student student = new(string.Empty);
Console.WriteLine(student.Name.ToLower());
Console.WriteLine(student.PhoneNumber!.ToLower());
对于可为空的引用类型的属性,编译器会在属性上添加NullableAttribute,因此可以在运行时通过反射判断一个引用类型属性的可空性。
很多.NET下的框架都充分利用了可为空的引用类型,从而对引用类型的属性、参数等进行更加智能化的处理。