Chapter 10. A smörgåsbord of features for concise code

覆盖using static、initializer增强、null conditional与exception filters,把简洁语法还原为name lookup、construction、null flow和exception ownership。

学习目标

  • 能解释using static和initializer增强分别改变name lookup与construction步骤,判断何时简洁反而隐藏owner或failure point
  • 能设计null conditional chain的short-circuit、nullable result与parentheses边界,并为每个null hop写出可观测测试
  • 能区分exception filter evaluation、stack unwinding与catch body执行时机,并实现清楚的handler ownership

机制总览

Chapter 10. A smörgåsbord of features for concise code:机制路径

  1. 1

    为什么简洁语法仍要展开成执行模型

    C 6把几种高频意图压进更短语法:导入static members、初始化indexer或已有child、沿member chain传播null、在catch前筛选异常。它们共同减少ceremony,却作用于完全不同阶段。评审时必须展开成四个问题:名字从哪解析、对象何时创建和赋值、哪一段会short-circuit、谁真正拥有异常。

  2. 2

    Using static directives

    using static System.Math; 让 Sqrt 、 PI 等static members可用unqualified name访问。它适合数学DSL、assertion helpers或一组非常稳定且语义鲜明的operations;若导入多个拥有同名成员的types,overload…

  3. 3

    Object and collection initial…

    C 6允许index initializer: new Dictionary ["apples"] = 3 。这与collection initializer的 Add 调用并不总是等价;indexer assignment可能覆盖existing key,而 Add 可能因duplicate ke…

先按顺序建立机制,再进入实验切换阶段并检查失效证据。

章级决策实验

Chapter 10. A smörgåsbord of features for concise code:机制与证据

切换《Chapter 10. A smörgåsbord of features for concise code》的三个关键教学阶段,先解释机制,再用运行与失败证据验证结论。

选择推理阶段

当前阶段 · 为什么简洁语法仍要展开成执行模型

C 6把几种高频意图压进更短语法:导入static members、初始化indexer或已有child、沿member chain传播null、在catch前筛选异常。它们共同减少ceremony,却作用于完全不同阶段。评审时必须展开成四个问题:名字从哪解析、对象何时创建和赋值、哪一段会short-circuit、谁真正拥有异常。

可核验证据

以明确的 LangVersion 与目标框架构建「为什么简洁语法仍要展开成执行模型」的正反案例,并用编译诊断、生成 IL、运行轨迹或分配数据核对实际边界。

学完《Chapter 10. A smörgåsbord of features for concise code》后,应能从输入和前置条件推导状态变化,并用可重复的构建、运行或边界测试证明结果。

失效—证据矩阵

Chapter 10. A smörgåsbord of features for concise code:失效与核验

为什么简洁语法仍要展开成执行模型

典型失效

若解释「为什么简洁语法仍要展开成执行模型」时混淆语言规范、编译器降级、运行时和类库责任,版本变化后就会把实现细节误当作 C# 语义保证。

核验证据

以明确的 LangVersion 与目标框架构建「为什么简洁语法仍要展开成执行模型」的正反案例,并用编译诊断、生成 IL、运行轨迹或分配数据核对实际边界。

Using static directives

典型失效

若解释「Using static directives」时混淆语言规范、编译器降级、运行时和类库责任,版本变化后就会把实现细节误当作 C# 语义保证。

核验证据

以明确的 LangVersion 与目标框架构建「Using static directives」的正反案例,并用编译诊断、生成 IL、运行轨迹或分配数据核对实际边界。

Object and collection initial…

典型失效

若解释「Object and collection initial…」时混淆语言规范、编译器降级、运行时和类库责任,版本变化后就会把实现细节误当作 C# 语义保证。

核验证据

以明确的 LangVersion 与目标框架构建「Object and collection initial…」的正反案例,并用编译诊断、生成 IL、运行轨迹或分配数据核对实际边界。

每个判断都必须能落到观测、测试或产物,不能只凭代码表面推测。

为什么简洁语法仍要展开成执行模型

C# 6把几种高频意图压进更短语法:导入static members、初始化indexer或已有child、沿member chain传播null、在catch前筛选异常。它们共同减少ceremony,却作用于完全不同阶段。评审时必须展开成四个问题:名字从哪解析、对象何时创建和赋值、哪一段会short-circuit、谁真正拥有异常。

先预测:using static是否导入nested types;nested initializer是否创建child;a?.B.C是否保护所有可能为null的hop;filter返回false时当前catch是否先unwind。把答案写下,再进入实验。正确结论分别是“只导入符合规则的static members、不是通用nested-type import”“不会创建已有child”“只保护当前conditional receiver且后续链需按语法分析”“不会选中该handler,会继续搜索”。

Using static directives

using static System.Math;SqrtPI等static members可用unqualified name访问。它适合数学DSL、assertion helpers或一组非常稳定且语义鲜明的operations;若导入多个拥有同名成员的types,overload resolution与ambiguity会变得不直观。关键不是“能不能少写Math”,而是reader能否立即知道owner与semantics。

Extension methods也可能随static import进入候选集合,但普通static member与extension lookup有不同规则。不要用大量imports构造隐形global vocabulary。对于security、time、random、serialization等owner本身就是重要信息的API,保留qualified call通常更清楚。

Scope review checklist

评审import时同时检查本文件现有calls和未来可合理新增的calls:同名member是否会产生ambiguity,method group是否会因overload改变binding,qualified owner是否承载domain信息。把这些写进review checklist,才能避免“今天能编译”被误当成长期可维护性证明。

using static System.Math;
 
double Hypotenuse(double x, double y) => Sqrt(Pow(x, 2) + Pow(y, 2));
// Review question: is Math still obvious at every call site in this file?

Object and collection initializer enhancements

C# 6允许index initializer:new Dictionary<string, int> { ["apples"] = 3 }。这与collection initializer的Add调用并不总是等价;indexer assignment可能覆盖existing key,而Add可能因duplicate key抛异常。Nested initializer可以配置已有property/indexer返回的对象,不代表自动new child。

Initializer仍是“先构造对象,再按source order执行assignments/Add/indexer calls”。Setter、Add或indexer任何一步抛异常,整个expression失败,partially initialized instance通常不会赋给左侧variable,但constructor/setters已发生的外部副作用不会回滚。Resource-owning object更要避免在initializer中混入不可逆操作。

分步1 / 3

切换qualified、using static、index与nested initializer

The null conditional operator

?.?[]只在receiver为null时跳过member/index access并产生null;非null时仍会执行member,member自身可以抛出其他异常。若最终member是non-nullable value type,conditional access把结果提升为nullable value。customer?.Orders?[0].Total中每个可能为null的receiver都要显式标出,index out of range也不会被吞掉。

Conditional chain受syntax约束。连续member access通常属于同一chain,但parentheses、assignment或后续ordinary access可能终止传播;(customer?.Address).City会在Address结果为null时重新发生ordinary access。Delegate invocation的Changed?.Invoke(...)表达“若没有subscriber则不调用”,但不处理subscriber内部异常,也不等于event delivery guarantee。

decimal? firstTotal = customer?.Orders?.FirstOrDefault()?.Total;
string city = customer?.Address?.City ?? "unknown";
Changed?.Invoke(this, EventArgs.Empty);
分步1 / 3

切换receiver、member、parentheses与delegate

Null conditional适合“absence是预期状态”的navigation。若null意味着corrupted invariant,静默传播会把fault推迟到更远处,应在boundary validate并抛带context的异常。简洁语法不能替代domain null policy。

Exception filters

catch (HttpRequestException ex) when (IsTransient(ex))把type match与diagnostic predicate组合。Filter在选择handler时执行;返回false会继续搜索外层或后续handlers。与进入catch后再ifthrow;相比,未选中handler前不执行它的body,runtime能保留更接近原始throw point的debugging状态。

try
{
    await SendAsync(request, cancellationToken);
}
catch (HttpRequestException ex) when (ex.StatusCode is >= 500)
{
    await retryQueue.EnqueueAsync(request, ex, cancellationToken);
}

Filter应快速、确定、尽量无副作用。Filter自身抛出的异常通常被视为filter false并继续search,这会隐藏predicate fault,因此需要独立测试和metrics。不要在filter里修改business state、执行network I/O或依赖会变化的global state。

分步1 / 3

切换type miss、false、true与filter throw

本章回顾:为每个短语法恢复它压缩的模型

  1. Using static缩短名字但扩大lookup surface,owner可读性与collision必须显式评估。
  2. Enhanced initializers是ordered calls,不是atomic literals;nested initializer也不会凭空创建child。
  3. Null conditional传播由syntax和每个receiver决定,不吞掉非null相关异常。
  4. Exception filters在unwind前选择ownership,false表示“本handler不负责”。
  5. Concision只有在边界、failure和tests仍清楚时才是改进。

练习

问题 1:一个文件导入Math和自定义Metrics的static members后出现同名Round,应如何决策?

问题 2:把order?.Customer.Address.City改成“绝不抛NullReferenceException”的正确测试是什么?

问题 3:只重试HTTP 5xx且不处理cancellation,filter与验收怎样写?

术语表

名词解释

本章出现的专业名词,用大白话再讲一遍。

lookup surface
construction contract
conditional access chain
diagnostic filter
exception ownership

原版目录概念补充核对

以下条目补齐官方目录中容易被示例主线掩盖的概念。它们不重复罗列目录,而是明确每项概念的机制、适用边界和验收证据。

The null conditional operator:机制、边界与证据

Chapter 10. A smörgåsbord of features for concise code中的The null conditional operator必须分清语言规范、编译器实现、运行时行为与基础类库 API 四层责任。固定 C# 语言版本和目标框架,用正向/负向编译案例、必要的 IL 或运行轨迹以及版本对照验证结论。

讨论

评论区加载中…