C#笔记——可为空值/引用类型

本文最后更新于:2023年9月7日 下午

可为空值类型

基本概念

可为空值类型T?表示其基础值类型T的所有值及额外的null值。比如bool?的值有:truefalsenull,而普通的bool的值只有:truefalse,因为基础值类型T本身不能是可为空的值类型。
任何可为空的值类型都是泛型System.Nullable<T>结构的实例,比如int?System.Nullable<int>的实例。

声明和赋值

声明就是基础值类型后加一个?,赋值则跟基础值类型一样。

1
bool? flag = null;

检查可为空值类型的实例

使用只读属性Nullable<T>.HasValue来检查和获取可为空值类型变量的值:

1
2
3
4
if (flag.HasValue)
{
...
}

从可为空的值类型转换为基础类型

如果要将可为空值类型的值分配给不可以为空的值类型变量,则可以使用Null合并操作符??或者Nullable<T>.GetValueOrDefault(T)方法(此方法若不传递参数则使用基础值类型的默认值来替代null):

1
2
3
4
5
6
7
int? a = 28;
int b = a ?? -1;
Console.WriteLine($"b is {b}."); // output: b is 28

int? c = null;
int d = c.GetValueOrDefault(-1);
Console.WriteLine($"d is {d}"); // output: d is -1

也可以使用显式强制转换为不可为空的类型(注意如果可为空的值类型的值为null的话,显式强制转换将抛出InvaildOperationException异常):

1
2
3
4
int? n = null;

//int m1 = n; // Doesn't compile
int n2 = (int)n; // Compiles, but throws an exception if n is null

提升的运算符

预定义的一元运算符和二元运算符或值类型T支持的任何重载运算符也受相应的可为空值类型T?支持。只要有一个操作数为null,那么运算的结果就是null。这里的特殊例子是bool?类型,详情可以参考官方文档。

1
2
3
4
int? a = 10;
int? b = null;

a = a + b; // a is null

对于比较运算符>>=<<=,只要有一个操作数为null,返回的结果就是false
对于相等运算符==,如果两个操作数都为null,则结果为true;如果只有一个操作数为null,则结果为false
对于不等运算符!=,参考相等运算符==

装箱和取消装箱

给可为空的值类型的实例进行装箱操作,实质上是对基础值类型T的对应值装箱:

  • 如果HasValue返回false,则生成空引用。
  • 如果HasValue返回true,则基础值类型T的对应值将装箱,而不对Nullable<T>的实例进行装箱。

如何确定可为空的值类型

下面的示例演示了如何确定System.Type实例是否表示已构造的可为空值类型:

1
2
3
4
5
6
7
8
Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

// Output:
// int? is nullable value type
// int is non-nullable value type

严格按照上面写就好了,先使用typeof运算符获取System.type实例,再使用Nullable.GetUnderlyingType方法来检查。如果想知道为什么,简洁来说就是Object.GetType方法获取到的Type实例无法区分,is运算符无法确定,详细的原因参考官方文档。

可为空引用类型

用来干什么?——用于降低代码导致运行时引发System.NullReferenceException异常(尝试取消引用空对象引用时引发的异常)的可能性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// System.NullReferenceException
using System;

public class Example
{
public static void Main()
{
int[] values = null;
for (int ctr = 0; ctr <= 9; ctr++)
values[ctr] = ctr * 2;

foreach (var value in values)
Console.WriteLine(value);
}
}
// The example displays the following output:
// Unhandled Exception:
// System.NullReferenceException: Object reference not set to an instance of an object.
// at Example.Main()

在可为null的感知上下文中:

  • 引用类型T的变量必须用非null值进行初始化,并且不能为其分配可能为null的值。
  • 引用类型T?的变量可以用null进行初始化,也可以分配null,但在取消引用之前必须对照null进行检查。
  • 类型为T?的变量m在应用null包容运算符(!)时被认为是非空的,如m!

限制

  • 可为空的引用类型不能用作基类或实现的接口
  • 可为空的引用类型不能用于任何对象创建
  • 可为空的引用类型不能用于任何类型测试表达式
  • 可为空的引用类型不能是成员访问表达式的类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public MyClass : System.Object? // not allowed
    {
    }

    var nullEmpty = System.String?.Empty; // Not allowed
    var maybeObject = new object?(); // Not allowed
    try
    {
    if (thing is string? nullableString) // not allowed
    Console.WriteLine(nullableString);
    } catch (Exception? e) // Not Allowed
    {
    Console.WriteLine("error");
    }

这里有一只爱丽丝

希望本文章能够帮到您~


C#笔记——可为空值/引用类型
https://map1e-g.github.io/2023/09/01/CSharp-learning-4/
作者
MaP1e-G
发布于
2023年9月1日
许可协议