Skip to content

C# - 可空类型

在编程中,null 表示值的缺失。正确处理 null 对于编写健壮且无 bug 的应用程序至关重要。C# 提供了一套强大的功能来管理 null,主要通过可空值类型(nullable value types)和可空引用类型(nullable reference types)实现。

默认情况下,像 int、double、bool 和 DateTime 这样的值类型不能为 null。它们必须始终持有一个值。然而,有时你需要表示一种“未赋值”或“未知”的状态,例如数据库中非必需的字段。

要允许值类型保存 null,你可以通过在类型名称后添加 ? 来将其声明为可空值类型。

// 普通的 int 不能为 null
// int age = null; // 编译时错误
// 可空 int 可以为 null
int? age = null;
bool? isOptedIn = true;
double? temperature = 21.5;
temperature = null; // 这是有效的

可空类型有两个重要的属性:

  • HasValue:一个布尔值,如果变量包含一个值,则为 true;如果为 null,则为 false。
  • Value:返回底层的值。如果你在 HasValue 为 false 时尝试访问它,会抛出 InvalidOperationException 异常。
int? middleNameLength = null;
if (middleNameLength.HasValue)
{
Console.WriteLine($"Length is: {middleNameLength.Value}");
}
else
{
Console.WriteLine("No middle name length provided.");
}

C# 8 中引入的可空引用类型(Nullable Reference Types, NRTs)是防止 NullReferenceException(最常见的运行时错误之一)的游戏规则改变者。此功能帮助你表达变量(如 string 或自定义类对象)是否允许为 null 的意图。

要启用 NRTs,请在你的 .csproj 项目文件中添加以下行:

<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>

启用 NRTs 后:

  • string name:这是一个不可为空的引用类型。如果你不初始化它或尝试将 null 赋值给它,编译器会发出警告。
  • string? nickname:这是一个可空的引用类型。它明确允许为 null。
#nullable enable // 你也可以为单个文件启用此功能
string requiredField = "Some value";
// requiredField = null; // 生成编译时警告!
string? optionalField = null; // 这是允许的
// 编译器会警告你 optionalField 在这里可能为 null!
// Console.WriteLine(optionalField.Length);
// 在使用之前你必须检查是否为 null
if (optionalField != null)
{
Console.WriteLine(optionalField.Length); // 这里没有警告
}

C# 提供了几个方便的运算符来处理可空类型。

?? 运算符为可空类型在为 null 时提供一个默认值。它读作“如果左侧不为 null,则使用它;否则,使用右侧的值。”

string? userName = null;
// 如果 userName 为 null,则使用 "Guest"。否则,使用 userName 的值。
string displayName = userName ?? "Guest";
Console.WriteLine(displayName); // 输出: Guest

??= 运算符(C# 8+)仅当左侧变量为 null 时,才将右侧值赋值给左侧变量。

List<int>? numbers = null;
// 如果 numbers 为 null,则创建一个新列表并将其赋值给 numbers。
numbers ??= new List<int>();
numbers.Add(42);
Console.WriteLine(numbers.Count); // 输出: 1

?. 和 ?[] 运算符(C# 6+)允许你仅当变量不为 null 时才访问其成员或元素。如果变量为 null,则表达式会短路并返回 null。

string? name = null;
// 如果 name 为 null,length 将为 null。否则,它将是 name.Length。
int? length = name?.Length;
Console.WriteLine(length); // 输出: (空,因为 length 为 null)
name = "Alice";
length = name?.Length;
Console.WriteLine(length); // 输出: 5
// 也适用于数组和索引器
string[]? names = { "Alice", "Bob" };
string? first = names?[0]; // "Alice"
names = null;
first = names?[0]; // first 变为 null,不会抛出异常

始终在你的现代 C# 项目中启用可空引用类型。它是提高代码质量和防止运行时错误的最有效的单个功能之一。