Chapter 7. C# 5 bonus features

覆盖Capturing variables in foreach loops与Caller information attributes,深入解释iteration storage、compiler-emitted arguments和diagnostic provenance边界。

学习目标

  • 能比较C# 5前后foreach iteration-variable lowering,并复现foreach、for与mutable captured object的不同结果
  • 能推导CallerMemberName/FilePath/LineNumber如何由caller compiler写入optional arguments,判断explicit override行为
  • 能设计property notification、logging与wrapper中的diagnostic provenance,避免把caller info当security或stable schema

机制总览

Chapter 7. C# 5 bonus features:机制路径

  1. 1

    为什么两个“小Feature”都在修复Hidden Sou…

    Closure bug来自reader以为每轮有新value,而compiler曾让delegates共享一个storage;caller info解决callee想知道call site,却不想每处重复脆弱string。两者都把source context变成明确compiler rule:一个调…

  2. 2

    Capturing variables in foreac…

    旧语言语义可理解为iteration variable位于loop外,每次赋新值;所有anonymous methods capture同一variable,所以延迟执行时看到最终value。C 5把foreach iteration variable视为每次iteration在body内新建,closures得到不同storage,使常见直觉成立。

  3. 3

    Caller information attributes

    CallerMemberName 、 CallerFilePath 、 CallerLineNumber 标注optional parameters。Call site省略argument时,compiler根据source context写入普通string/int;callee只接收values…

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

章级决策实验

Chapter 7. C# 5 bonus features:机制与证据

切换《Chapter 7. C# 5 bonus features》的三个关键教学阶段,先解释机制,再用运行与失败证据验证结论。

选择推理阶段

当前阶段 · 为什么两个“小Feature”都在修复Hidden Sou…

Closure bug来自reader以为每轮有新value,而compiler曾让delegates共享一个storage;caller info解决callee想知道call site,却不想每处重复脆弱string。两者都把source context变成明确compiler rule:一个调…

可核验证据

以明确的 LangVersion 与目标框架构建「为什么两个“小Feature”都在修复Hidden Sou…」的正反案例,并用编译诊断、生成 IL、运行轨迹或分配数据核对实际边界。

学完《Chapter 7. C# 5 bonus features》后,应能从输入和前置条件推导状态变化,并用可重复的构建、运行或边界测试证明结果。

失效—证据矩阵

Chapter 7. C# 5 bonus features:失效与核验

为什么两个“小Feature”都在修复Hidden Sou…

典型失效

若解释「为什么两个“小Feature”都在修复Hidden Sou…」时混淆语言规范、编译器降级、运行时和类库责任,版本变化后就会把实现细节误当作 C# 语义保证。

核验证据

以明确的 LangVersion 与目标框架构建「为什么两个“小Feature”都在修复Hidden Sou…」的正反案例,并用编译诊断、生成 IL、运行轨迹或分配数据核对实际边界。

Capturing variables in foreac…

典型失效

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

核验证据

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

Caller information attributes

典型失效

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

核验证据

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

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

为什么两个“小Feature”都在修复Hidden Source Context

Closure bug来自reader以为每轮有新value,而compiler曾让delegates共享一个storage;caller info解决callee想知道call site,却不想每处重复脆弱string。两者都把source context变成明确compiler rule:一个调整variable scope/lifetime,一个把call-site metadata烤进arguments。

先预测:C# 5修复foreach后for loop capture是否也改变;每轮iteration variable不同是否意味着captured object immutable;CallerFilePath是否runtime stack读取;caller info是否不可伪造。答案都是否。

Capturing variables in foreach loops

旧语言语义可理解为iteration variable位于loop外,每次赋新值;所有anonymous methods capture同一variable,所以延迟执行时看到最终value。C# 5把foreach iteration variable视为每次iteration在body内新建,closures得到不同storage,使常见直觉成立。

var actions = new List<Action>();
foreach (var value in new[] { "a", "b", "c" })
{
    actions.Add(() => Console.WriteLine(value));
}
// C# 5+: a, b, c

The fix is deliberately narrow

for (var i=0; ...)的i仍是一个loop variable,capture后通常看到loop结束值;需要body-local copy。Foreach修的是iteration-variable declaration semantics,不是“所有loop capture按值”。Code review必须识别实际variable declaration scope。

即使每轮variable storage不同,variable若保存mutable object reference,closures仍看到object后续mutation。想要snapshot应capture immutable value、clone owned data或提前project;“不同reference variable”不保证“不同object state”。

Version and compatibility

同一source用旧/new compiler可能产生不同observable behavior,这正是language breaking change的罕见例子,因旧行为几乎总是bug。Legacy binary不因runtime升级重写closure;只有recompile按新compiler semantics生成。Migration测试要区分existing binary与rebuilt source。

分步1 / 3

切换旧foreach、新foreach、for与mutable item

Caller information attributes

CallerMemberNameCallerFilePathCallerLineNumber标注optional parameters。Call site省略argument时,compiler根据source context写入普通string/int;callee只接收values,不inspect stack。Explicit argument优先,因此它们是convenience diagnostics,不是tamper-proof identity。

static void Trace(
    string message,
    [CallerMemberName] string member = "",
    [CallerFilePath] string file = "",
    [CallerLineNumber] int line = 0) { }

Member name适合INotifyPropertyChanged helper,减少rename后string drift;file/line可辅助logging,但absolute source path可能泄露build machine/目录并增加cardinality。Release pipeline应normalize/redact,需要stable event schema时用explicit event id而不是source line。

Forwarding wrapper若希望保留original caller,自己也要暴露caller-info optional parameters并显式传给inner method;否则inner compiler填的是wrapper member/file/line。每增加一层都测试provenance,避免日志看似精确却指向helper。

分步1 / 3

切换member、file、line与modern expression

Diagnostic provenance的正确边界

Property notification关心的是property contract;caller member name在property setter helper里合适,但若一个method代表多个properties,应显式传name。Logging关心event identity与operation context;source location只是补充。Guard message可在现代版本用CallerArgumentExpression显示source expression,但它属于C# 10 update,不是第4版C# 5内容。

Caller info values会bake进caller binary:source移动line或rename member后,旧binary仍携带旧values,新build产生新values。Aggregating metrics不应以line/file为长期key。Symbol/PDB与runtime stack各解决其他diagnostic问题,不能互相替代。

void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
分步1 / 3

切换property、logging、guard与forwarding

本章回顾:Compiler Context是便利证据,不是权威事实

  1. C# 5 foreach每轮创建独立iteration-variable storage,修复常见closure结果。
  2. For loop variable未改变,captured object也仍可能mutable;必须分开storage与object state。
  3. Caller info由caller compiler写入optional arguments,不是runtime stack inspection。
  4. Wrapper需显式forward original context,file path要privacy/cardinality治理。
  5. Caller metadata适合diagnostics和notification,不适合security identity或stable event key。

练习

问题 1:为何C# 5 foreach输出正确,for loop仍可能全输出最终index?

问题 2:CallerFilePath可以直接作为日志聚合维度吗?

问题 3:两层logging wrapper怎样保留最外层caller?

术语表

名词解释

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

captured storage model
per-iteration variable semantics
compiler-emitted caller argument
diagnostic provenance
non-authoritative source metadata

资料与写作方式声明

本章以C# in Depth, Fourth Edition, Chapter 7: C# 5 bonus features权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…