C# Null 安全性

小窍门

本文是已了解至少一种编程语言并正在学习 C# 的开发人员的 “基础知识 ”部分的一部分。 如果你不熟悉编程,请先 学习入门 教程。

来自 Java 或 C++? C# 通过可为 null 的引用类型在编译时提供 null 安全。 目标类似于Java的 @NonNull 批注,但由编译器强制执行。 C# 还具有 ?.?? 等专用运算符,使 null 安全的表达式更加简洁。

null 表示缺少值。 当你试图通过调用方法或读取属性来访问 null 引用上的成员时,运行时会抛出一个 NullReferenceException

// Accessing a member on null throws NullReferenceException at runtime:
// string? name = null;
// int length = name.Length; // throws NullReferenceException

// Check before you dereference:
string? name = null;
if (name is not null)
{
    Console.WriteLine($"Name has {name.Length} characters.");
}
else
{
    Console.WriteLine("Name has no value.");
}
// Output: Name has no value.

C# 提供了三个补充工具,用于编写 null 安全代码:

  • 可以为 Null 的值类型:让 intbool 这样的值类型也保持 null
  • 可以为 null 的引用类型:让编译器跟踪引用是否可能是 null
  • Null 运算符:简明表示 null 安全访问和回退逻辑

可以为 null 的值类型

值类型,例如 intdoublebool,在默认情况下无法保存 null。 向类型名称添加 ? 以创建可为 null 的值类型,该类型可以包含一个值或

int? score = null;
Console.WriteLine(score.HasValue);               // False

score = 95;
Console.WriteLine(score.HasValue);               // True
Console.WriteLine(score.GetValueOrDefault());    // 95

int? missing = null;
Console.WriteLine(missing.GetValueOrDefault(-1)); // -1

可空值类型在基本值类型需要表示“无数据”时非常有用。典型场景包括可能不存在的数据库列、可选的配置设置,以及尚未捕获的传感器读数。

有关声明、检查和转换的完整覆盖范围,请参阅可以为 null 的值类型

可为空引用类型

引用类型(如 string数组和类实例)可以在运行时保留 null可为 null 的引用类型是一种编译器特性,它明确表示引用是否可以为 null,并在编译时捕获错误。

通过使用 ? 批注,声明意向:

  • string?— 此引用可能null;如果不先检查就取消引用它,编译器会发出警告
  • string — 此引用 不应null;如果分配给 null 该引用,编译器将发出警告
// string?  means this reference might be null
// string   means this reference should not be null
string? nullableName = null;
string  nonNullName  = "Alice";

// ?. safely accesses a member when the reference might be null
string display = nullableName?.ToUpper() ?? "(no name)";
Console.WriteLine(display);         // (no name)

display = nonNullName.ToUpper();    // safe: nonNullName is never null
Console.WriteLine(display);         // ALICE

新式 SDK 模板创建的所有.NET项目默认启用可为 null 的引用类型。 有关启用和批注的完整指南,请参阅可为 null 的引用类型

空值运算符

C# 包含几个运算符,可让你在任何地方编写不带手动 if-null 防护的 null 安全代码:

运算符 Name Purpose
?. Null 条件成员访问 仅当对象为非 null 时访问成员
?[] null 条件索引器访问 仅当集合为非 null 时访问元素
?? null 合并 当表达式为 null 时返回回退值
??= Null 合并赋值 仅在变量为 null 时进行赋值
is null / is not null Null 模式 首选空值测试
string? city = GetCity();

// ?. — access a member only when non-null
int? len = city?.Length;

// ?? — substitute a default when null
string display = city ?? "unknown";

// is null — preferred null test
if (city is null)
{
    Console.WriteLine("No city provided.");
}
else
{
    Console.WriteLine($"{display} ({len} chars)");
}
// Output: No city provided.

有关每个运算符的详细示例,请参阅 Null 运算符

可空值类型和可空引用类型用于不同的目的

可以为 null 的值类型和可为 null 的引用类型不是替代方法。 他们解决了不同的问题:

  • T? 用于表示“无值”的值类型。例如,int? 可用于表示可选数据库列,DateTime? 可用于表示尚未计划的事件。
  • 使用 string? 和其他可为 null 的引用批注来记录一个引用可能null,从而使编译器能够在运行时发生 NullReferenceException 之前发出警告。

这些功能和 null 运算符一起提供了一组完整的工具来编写 null 安全的 C# 代码。