Chapter 1: C# Language Idioms (Items 1-10)

完整覆盖第3版Item 1-10:局部类型推断、readonly、pattern/cast、字符串与文化、强类型API、delegate/event、装箱及new成员隐藏。

学习目标

  • 能判断可读性、静态类型、版本边界和culture ownership,选择var、readonly、pattern、interpolation与FormattableString
  • 能设计compiler可检查且lifetime明确的command、callback和event API contract
  • 能分析boxing allocation与new member hiding,并用generic或virtual dispatch及针对性测试修正

为什么语言习惯必须让类型表达决策

本章的10条建议并非追求短语法,而是让source code保留足够的type、culture、ownership与dispatch信息。var可以减少重复,interpolation可以让template贴近value,但只要它们遮住numeric width、cross-culture contract或runtime allocation,就应改用更明确的表达。

先预测:把所有local改成var、把所有public value改成const、把所有type mismatch改成as,是否仍能保留相同的版本与失败contract?逐项答案都是否。

Type、Value与Text边界(Items 1-5)

Item 1: Prefer Implicitly Typed Local Variables

右侧构造、LINQ projection或anonymous type已经清楚给出类型时用var,避免把复杂generic type重复两遍。若method name没有揭示返回的precision、ownership或abstraction,显式类型更利于review。验收不是“是否全部var”,而是reader能否在当前行推断contract。

Item 2: Prefer readonly to const

const值会编译进consumer,public library改值后旧consumer仍使用旧literal;static readonly在runtime从定义assembly读取,适合可能演进的domain value。真正跨版本永不变化且需要constant expression的值才用const

public static class Limits
{
    public static readonly TimeSpan RequestTimeout = TimeSpan.FromSeconds(5);
    public const int BytesPerKilobyte = 1024;
}

Item 3: Prefer the is or as Operators to Casts

当mismatch是预期branch时,declaration pattern同时测试和bind:if (message is Invoice invoice)。当contract保证类型必须匹配时,explicit cast可fail fast。as只适用于reference/nullable并把mismatch变成null,必须避免与“合法null”混淆。

Item 4: Replace string.Format() with Interpolated Strings

Interpolation把placeholder与expression并置,重排参数时不易错;但它不会自动解决logging template、localization或invariant serialization。Structured logging应保留property names,machine protocol应使用stable serializer,而非把任何文本都插值化。

Item 5: Prefer FormattableString for Culture-Specific Strings

FormattableString保留composite format与typed arguments,让最终sink选择culture。UI通常在边界使用用户culture;signature、cache key、wire value则显式invariant。不要先产生普通string再尝试恢复丢失的culture intent。

分步1 / 3

切换五种expression与formatting场景

Strong Contract与Invocation边界(Items 6-8)

Item 6: Avoid String-ly Typed APIs

用string传command、member name或state会把拼写错误推迟到runtime。优先enum、value object、generic type、expression或具体command;若external protocol只能是string,在adapter处parse/validate并尽快转成domain type。

Item 7: Express Callbacks with Delegates

Delegate同时声明参数、返回值和同步调用形态,比“method name string + reflection”可检查。选择Action/Func用于通用签名,domain语义强或需文档时声明named delegate。还要说明callback是否可重入、是否允许抛异常、在哪个thread调用。

Item 8: Use the Null Conditional Operator for Event Invocations

Changed?.Invoke(this, args)只读取一次event delegate,并清楚表达“无subscriber则不调用”。这不等于完整event contract:publisher仍需确定thread、subscriber exception、ordering和unsubscribe;跨thread修改subscription时还要按实际concurrency model验证。

public event EventHandler<PriceChangedEventArgs>? PriceChanged;
 
private void RaisePriceChanged(decimal before, decimal after) =>
    PriceChanged?.Invoke(this, new(before, after));
分步1 / 3

比较string command、delegate、event和raise

Runtime Cost与Inheritance演进(Items 9-10)

Item 9: Minimize Boxing and Unboxing

Value type转成object或某些interface representation时会boxing并可能分配;unboxing要求exact boxed type。优先generic collection/API保留T,hot path再用allocation profiler验证。不要为消除一次冷路径boxing制造复杂unsafe design。

Item 10: Use the new Modifier Only to React to Base Class Updates

new不是polymorphic override,它明确隐藏base同名member,调用目标取决于reference的compile-time type。典型合法场景是base library升级后新增同名member,而derived type必须保留既有semantics;仍应审计全部call sites。需要substitutability时应由base virtual、derived override建立同一slot。

Base value = new Derived();
value.Render(); // override: Derived.Render; new: Base.Render
分步1 / 3

切换boxing与dispatch案例

本章回顾:语法选择必须留下可验证的Contract

  1. var保留static type,但可读性由当前表达式是否揭示contract决定。
  2. readonly避免public const的literal baking;culture由最终格式化边界显式拥有。
  3. Pattern表达预期shape branch,explicit cast表达contract violation。
  4. Type、delegate和event把字符串协议升级为compiler proof,但仍需lifetime和thread policy。
  5. Generic API减少boxing;new只明确隐藏,不替代override

练习

问题 1:library公开timeout值,UI和签名日志分别如何选择readonly与culture?

问题 2:怎样把Run("price", callbackName)改成可验收的强类型API?

问题 3:何时修boxing,何时用new,分别需要什么证据?

术语表

名词解释

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

local type inference
culture ownership
stringly typed API
boxing
member hiding

讨论

评论区加载中…