可为 null 值类型T? 表示其基础值类型T 的所有值及额外的 null 值。 例如,可以将以下三个值中的任意一个指定给 bool? 变量:true、false 或 null。 基础值类型 T 不能是可为 null 的值类型本身。
C# 语言参考记录了最近发布的 C# 语言版本。 它还包含即将发布的语言版本公共预览版中功能的初始文档。
本文档标识了在语言的最后三个版本或当前公共预览版中首次引入的任何功能。
小窍门
若要查找 C# 中首次引入功能时,请参阅 有关 C# 语言版本历史记录的文章。
任何可为空的值类型都是泛型 System.Nullable<T> 结构的实例。 可使用以下任何一种可互换形式引用具有基础类型 T 的可为空值类型:Nullable<T> 或 T?。
通常,如果需要表示基础值类型的未定义值,请使用可为 null 的值类型。 例如,布尔值或 bool 变量只能为 true 或 false。 但是,在某些应用程序中,变量值可能未定义或缺失。 例如,数据库字段可能包含 true 或 false,或者它可能根本不包含任何值,即 NULL。 在这种情况下,可以使用 bool? 类型。
声明和赋值
由于值类型可隐式转换为相应的可为 null 值类型,因此可以像为其基础值类型那样向可为 null 的值类型的变量赋值。 还可分配 null 值。 例如:
double? pi = 3.14;
char? letter = 'a';
int m2 = 10;
int? m = m2;
bool? flag = null;
// An array of a nullable value type:
int?[] arr = new int?[10];
可以为 null 的值类型的默认值表示 null。 它是一个实例,其 Nullable<T>.HasValue 属性返回 false。
检查可为空值类型的实例
若要检查可为 null 的值类型的 null 实例并获取基础类型的值,请使用 is 具有类型模式的运算符:
int? a = 42;
if (a is int valueOfA)
{
Console.WriteLine($"a is {valueOfA}");
}
else
{
Console.WriteLine("a does not have a value");
}
// Output:
// a is 42
始终可以使用以下只读属性来检查和获取可为 null 的值类型变量:
Nullable<T>.HasValue 显示可为 null 的值类型的实例是否具有其基础类型的值。
如果 Nullable<T>.Value 为 HasValue,则
true获取基础类型的值。 如果 HasValue 为false,则 Value 属性将引发 InvalidOperationException。
以下示例使用 HasValue 该属性检查变量是否包含一个值,然后再显示该值:
int? b = 10;
if (b.HasValue)
{
Console.WriteLine($"b is {b.Value}");
}
else
{
Console.WriteLine("b does not have a value");
}
// Output:
// b is 10
还可将可为空的值类型的变量与 null 进行比较,而不是使用 HasValue 属性,如以下示例所示:
int? c = 7;
if (c != null)
{
Console.WriteLine($"c is {c.Value}");
}
else
{
Console.WriteLine("c does not have a value");
}
// Output:
// c is 7
从可为空的值类型转换为基础类型
如果要将可为 null 值类型的值赋给不可为 null 的值类型变量,则可能需要指定要分配的值来代替 null。 使用 null 合并运算符 ?? 执行此作。 还可以将 Nullable<T>.GetValueOrDefault(T) 该方法用于相同目的:
int? a = 28;
int b = a ?? -1;
Console.WriteLine($"b is {b}"); // output: b is 28
int? c = null;
int d = c ?? -1;
Console.WriteLine($"d is {d}"); // output: d is -1
如果要使用基础值类型的默认值来替代 null,请使用 Nullable<T>.GetValueOrDefault() 方法。
还可以将可为空的值类型显式强制转换为不可为 null 的类型,如以下示例所示:
int? n = null;
//int m1 = n; // Doesn't compile
int n2 = (int)n; // Compiles, but throws an exception if n is null
在运行时,如果可为空的值类型的值为 null,则显式强制转换将抛出 InvalidOperationException。
不可为 null 的值类型 T 隐式转换为相应的可为空值类型 T?。
提升的运算符
可为 null 的值类型 T? 支持预定义的一元运算符和二进制 运算符 或值类型 T 支持的任何重载运算符。 这些运算符(也称为提升运算符)null返回一个或两个作数。null 否则,运算符使用其作数的包含值来计算结果。 例如:
int? a = 10;
int? b = null;
int? c = 10;
a++; // a is 11
a = a * c; // a is 110
a = a + b; // a is null
注意
对于 bool? 类型,预定义的 & 和 | 运算符不遵循此部分中描述的规则:即使其中一个操作数为 null,运算符计算结果也可以不为 NULL。 有关详细信息,请参阅布尔逻辑运算符一文的可以为 null 的布尔逻辑运算符部分。
对于比较运算符,><=如果>=一个或两个作<数为null,则结果是false。 否则,将比较作数的包含值。 不要假设因为特定的比较(例如) <=返回 false,因此相反的比较(>)返回 true。 以下示例显示 10
- 既不大于等于
null, - 也不小于
null
int? a = 10;
Console.WriteLine($"{a} >= null is {a >= null}");
Console.WriteLine($"{a} < null is {a < null}");
Console.WriteLine($"{a} == null is {a == null}");
// Output:
// 10 >= null is False
// 10 < null is False
// 10 == null is False
int? b = null;
int? c = null;
Console.WriteLine($"null >= null is {b >= c}");
Console.WriteLine($"null == null is {b == c}");
// Output:
// null >= null is False
// null == null is True
对于相等运算符,如果两个作==数均为null,则结果为 true。 如果只有一个作数 null,则结果是 false。 否则,将比较作数的包含值。
对于 不相等运算符!=,如果两个作数都是 null,则结果是 false。 如果只有一个作数 null,则结果是 true。 否则,将比较作数的包含值。
如果 用户定义的转换 存在于两个值类型之间,则同一转换也可以在相应的可以为 null 的值类型之间使用。
装箱和取消装箱
将可以为 null 的值类型的T?实例装箱时,适用以下规则:
- 如果 HasValue 返回
false,装箱作将返回 null 引用。 - 如果 HasValue 返回
true,则装箱作将装箱为基础值类型的相应值,而不是基础值类型的T实例 Nullable<T>。
可将值类型 T 的已装箱值取消装箱到相应的可为空值类型 T?,如以下示例所示:
int a = 41;
object aBoxed = a;
int? aNullable = (int?)aBoxed;
Console.WriteLine($"Value of aNullable: {aNullable}");
object aNullableBoxed = aNullable;
if (aNullableBoxed is int valueOfA)
{
Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
}
// Output:
// Value of aNullable: 41
// aNullableBoxed is boxed int: 41
如何确定可为空的值类型
下面的示例演示了如何确定 System.Type 实例是否表示已构造的可为空值类型,即,具有指定类型参数 System.Nullable<T> 的 T 类型:
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 实例。
如果要确定实例是否为可以为 null 的值类型,请不要使用 Object.GetType 该方法获取 Type 实例以使用前面的代码进行测试。 如果对值类型可为空的实例调用 Object.GetType 方法,该实例将装箱到 Object。 由于装箱可为 null 的值类型的非 null 实例等效于装箱基础类型的值, GetType 因此返回表示 Type 可为 null 值类型的基础类型的实例:
int? a = 17;
Type typeOfA = a.GetType();
Console.WriteLine(typeOfA.FullName);
// Output:
// System.Int32
另外,请勿使用 is 运算符来确定实例是否是可为空的值类型。 如以下示例所示,无法使用运算符来区分可以为 null 的值类型实例及其基础类型实例 is 的类型:
int? a = 14;
if (a is int)
{
Console.WriteLine("int? instance is compatible with int");
}
int b = 17;
if (b is int?)
{
Console.WriteLine("int instance is compatible with int?");
}
// Output:
// int? instance is compatible with int
// int instance is compatible with int?
请改用 Nullable.GetUnderlyingType 第一个示例中的方法和 typeof 运算符来检查实例是否为可以为 null 的值类型。
注意
本节中所述的方法不适用于 可为 null 的引用类型。
C# 语言规范
有关更多信息,请参阅 C# 语言规范的以下部分: