0Pricing
C# Academy · 课时

空值条件与空值合并运算符

组合使用 ?、?[]、?? 和 ??=,编写简洁且空值安全的代码,避免冗长的空值检查。

空值条件与空值合并运算符 是 CoddyKit 上的免费 C# Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。

空值安全代码模式

C# 提供了三个专门用于简洁处理空值的运算符:空值条件运算符(?.、?[])、空值合并运算符(??)以及空值合并赋值运算符(??=)。它们结合使用,可以消除大多数冗长的空值检查样板代码。

空值条件运算符:?.

如果左侧为空值,?. 会短路并返回 null,而不是抛出 NullReferenceException。结果类型会变为可空类型。

User? user = GetUser(id);

// Without ?.:
string? name = null;
if (user != null) name = user.Name;

// With ?.:
string? name2 = user?.Name;

// Chained:
string? city = user?.Address?.City;

// Method call:
string? upper = user?.Name?.ToUpper();

带索引访问器的空值条件运算符:?[]

?[] 是空值条件索引运算符。当数组或集合本身可能为空时,它可以安全地访问其中的元素。

List<string>? tags = product?.Tags;

// Safe index access
string? firstTag = tags?[0];

// Null-conditional LINQ
int? count = tags?.Count;
bool? hasItems = tags?.Any();

// With methods:
string? joined = tags?.FirstOrDefault()?.ToUpper();

空值合并运算符:??

如果左操作数不为空,?? 就返回左操作数;否则返回右操作数(回退值)。它是 x != null ? x : fallback 的简洁替代写法。

string? name = GetName();

// Without ??:
string display = name != null ? name : "Anonymous";

// With ??:
string display2 = name ?? "Anonymous";

// Chaining ?? for multiple fallbacks:
string result = primary ?? secondary ?? tertiary ?? "Default";

// Combining with ?.
string city = user?.Address?.City ?? "Unknown City";

空值合并赋值:??=

??= 仅当变量当前为 null 时,才会将右侧的值赋给该变量。它非常适合用于延迟初始化模式。

// Lazy initialization
private List<string>? _cache;

public List<string> GetCache()
{
    _cache ??= new List<string>(); // only assigns if null
    return _cache;
}

// Equivalent to:
// if (_cache is null) _cache = new List<string>();

// In-place:
string? name = null;
name ??= "Default";
Console.WriteLine(name); // "Default"

在事件中使用 ?.

空值条件运算符是调用事件的标准线程安全方式,可避免先单独进行 null 检查、再调用事件所产生的竞争条件。

public event EventHandler<DataEventArgs>? DataReceived;

// Thread-safe event invocation:
DataReceived?.Invoke(this, new DataEventArgs(data));

// This is equivalent to:
var handler = DataReceived;
if (handler != null) handler(this, new DataEventArgs(data));
// (local copy avoids race condition between check and invoke)

复杂链式示例

组合使用这些运算符,可以简洁、易读且安全地遍历深层嵌套的对象图,同时避免 null。

var order = GetOrder(id);

// Deeply nested with fallbacks:
string countryCode = order
    ?.Customer
    ?.ShippingAddress
    ?.Country
    ?.Code
    ?? "US";

// Collection safe access:
decimal firstLineTotal = order
    ?.Lines
    ?.FirstOrDefault()
    ?.Total
    ?? 0m;

// Method chain:
string? trackingUpper = order?.TrackingNumber?.ToUpper().Trim();

LINQ 中的空值条件运算符

空值条件运算符可以与 LINQ 无缝协作,从而安全地查询可能为 null 的集合或属性。

List<Order>? orders = customer?.Orders;

// Safe LINQ on a nullable collection:
var totalRevenue = orders?.Sum(o => o.Total) ?? 0m;
var pendingCount = orders?.Count(o => o.Status == OrderStatus.Pending) ?? 0;
var latest       = orders?.MaxBy(o => o.PlacedAt)?.Id;

使用 ThrowIfNull 进行空值保护

对于不允许为 null 的参数,请使用 ArgumentNullException.ThrowIfNull()(.NET 6+),在方法入口处以简洁且清晰的方式完成验证。

public void ProcessOrder(Order order, Customer customer)
{
    ArgumentNullException.ThrowIfNull(order);
    ArgumentNullException.ThrowIfNull(customer);

    // From this point, compiler knows both are non-null
    Console.WriteLine(order.Id);
    Console.WriteLine(customer.Name);
}

// .NET 7+: ArgumentException.ThrowIfNullOrEmpty
void Save(string name)
{
    ArgumentException.ThrowIfNullOrWhiteSpace(name);
    // name is guaranteed non-empty
}

Switch 中的非空模式

使用 is not null 和 is { } 进行模式匹配,可以构建简洁的 null 检查模式,尤其适用于 switch 表达式。

string Describe(object? obj) => obj switch
{
    null                          => "nothing",
    string s when s.Length == 0  => "empty string",
    string s                      => $"string: {s}",
    int i                         => $"integer: {i}",
    { }                           => $"object: {obj.GetType().Name}"
};

// is not null in if statements:
if (order is not null && order.Customer is { Name: var name })
    Console.WriteLine(name);

快速检查

当 user 为 null 时,表达式 user?.Name ?? "Anonymous" 会返回什么?

总结:空值条件与空值合并

关键要点:

  • ?.:安全的成员访问——接收对象为 null 时返回 null,而不是抛出异常
  • ?[]:安全地访问可能为 null 的集合中的索引
  • ??:左操作数为 null 时使用回退值
  • ??=:仅在值为 null 时赋值——非常适合延迟初始化
  • 将它们串联起来:使用 a?.B?.C ?? defaultVal 深入遍历对象,同时安全处理 null
  • 使用 ArgumentNullException.ThrowIfNull() 为参数添加验证保护

常见问题解答

「空值条件与空值合并运算符」课时是免费的吗?

是的 — 「空值条件与空值合并运算符」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。

「空值条件与空值合并运算符」这节课中我会学到什么?

组合使用 ?、?[]、?? 和 ??=,编写简洁且空值安全的代码,避免冗长的空值检查。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C# Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「空值条件与空值合并运算符」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C# Academy 课中编写并运行代码吗?

能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 启用并理解 NRT
  2. 注解:?、!、MaybeNull 与 NotNull
  3. 空值条件与空值合并运算符
  4. 将代码库迁移到 NRT
← 返回 C# Academy