小窍门
本文是已了解至少一种编程语言并正在学习 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 的值类型:让
int或bool这样的值类型也保持null -
可以为 null 的引用类型:让编译器跟踪引用是否可能是
null - Null 运算符:简明表示 null 安全访问和回退逻辑
可以为 null 的值类型
值类型,例如 int、double 和 bool,在默认情况下无法保存 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# 代码。