可空类型允许我们创建一个值类型变量并且可以标记为有效或无效,这样我们就可以有效的把值类型设置为“null”,在处理数据库和其他包含可能未赋值的元素的数据类型时,将null赋值给数值类型或布尔类型的功能特别有用,例如数据库中的布尔类型字段可以存储值true或false,或者该字段也可以未定义。
可空类型总是基于另外一个叫做基础类型的已经被声明的类型,可以从任何值类型创建可空类型,包括预定义的简单类型,不能从引用类型或其他可空类型创建可空类型。不能在代码中显式声明可空类型,只能声明可空类型的变量,之后我们会看到,编译器会使用泛型隐式地创建可空类型,创建可空类型的变量,只需要在变量声明中的基础类型的名字后面加一个问号。
static void Main(string[] args)
{
//a i b值都为null
int? a = null;
int? i = new int?();
bool? b = new bool?();
int? myNInt = 100;
if (myNInt != null) //直接与null比较
Console.WriteLine(myNInt);
if(myNInt.HasValue) //通过检查它的HasValue属性
Console.WriteLine(myNInt);
}
可空类型与非可空类型之间的转换
(1)非可空类型--->可空类型 为隐式,不需要强制转换
(2)可空类型--->非可空类型 需要显式转换
空接合运算符(??)
空接合运算符由俩个连续的问号组成,它允许我们在可空类型变量为null时返回一个值给表达式,它有俩个操作数,第一个操作数是可空类型的变量,第二个是相同基础类型的不可空值,在运行时,如果第一个操作数运算后为null,那么第二个操作数就会被返回为运算符结果。简单来讲就是如果??左边操作数为null,则返回??右边的操作数。
static void Main(string[] args)
{
int? i = null;
Console.WriteLine(i ?? -1); //-1
i = 4;
Console.WriteLine(i ?? -1); //4
}
可空用户自定义类型
我们可以创建用户自定义值类型的可空形式,它的主要问题是访问封装的基础类型的成员,一个可空类型不直接暴露基础类型的任何成员,例如struct的字段是公共的,它可以被结构的任何实例所访问到,然而,结构的可空形式只通过Value属性暴露基础类型,它不直接暴露它的任何成员,尽管这些成员对结构来说是公共的,但是它们对可空类型来说不是公共的。
struct S
{
public int X;
public int Y;
public S(int xVal,int yVal)
{
X = xVal; Y = yVal;
}
}
static void Main(string[] args)
{
S? s = new S(5, 10);
S s1 = new S(6, 12);
Console.WriteLine("{0} {1}", s1.X, s1.Y); //非可空类型的访问
Console.WriteLine("{0} {1}", s.Value.X, s.Value.Y); //可空类型的访问
}
Nullable<T>
可空类型通过一个叫做Syatem.Nullable<T>的.NET类型来实现,它使用了C#的泛型特性,C#可空类型的问号语法是创建Nullable<T>类型变量的快捷语法,Nullable<T>接受了基础类型并把它嵌入结构中,同时给结构提供可空类型的属性、方法和构造函数。
public class Program
{
struct S
{
public int X;
public int Y;
public S(int xVal,int yVal)
{
X = xVal; Y = yVal;
}
}
static void Main(string[] args)
{
S? s = new S(5, 10);
Nullable<S> s1 = new Nullable<S>((S)s);
Console.WriteLine("{0} {1}", s.Value.X, s.Value.Y);
Console.WriteLine("{0} {1}", s1.Value.X, s1.Value.Y);
}
}