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
为什么简洁语法仍要展开成执行模型
C 6把几种高频意图压进更短语法:导入static members、初始化indexer或已有child、沿member chain传播null、在catch前筛选异常。它们共同减少ceremony,却作用于完全不同阶段。评审时必须展开成四个问题:名字从哪解析、对象何时创建和赋值、哪一段会short-circuit、谁真正拥有异常。
- 2
Using static directives
using static System.Math; 让 Sqrt 、 PI 等static members可用unqualified name访问。它适合数学DSL、assertion helpers或一组非常稳定且语义鲜明的operations;若导入多个拥有同名成员的types,overload…
- 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;让Sqrt、PI等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中混入不可逆操作。
↡constructor、setter、Add与indexer共同定义对象从未构造到可用状态的顺序、验证与失败规则。切换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);切换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后再if并throw;相比,未选中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。
↡catch type与when predicate共同声明当前handler愿意负责的failure subset。 ↡一个layer是否能恢复、转换、补充上下文或终止某类failure的明确责任。切换type miss、false、true与filter throw
本章回顾:为每个短语法恢复它压缩的模型
- Using static缩短名字但扩大lookup surface,owner可读性与collision必须显式评估。
- Enhanced initializers是ordered calls,不是atomic literals;nested initializer也不会凭空创建child。
- Null conditional传播由syntax和每个receiver决定,不吞掉非null相关异常。
- Exception filters在unwind前选择ownership,false表示“本handler不负责”。
- 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 或运行轨迹以及版本对照验证结论。