知

模块六 · AI 协作与表达

第 11 / 11 节19 分钟阅读型

面试表达:把 .NET 讲成工程取舍

.NET 面试不是背 API,而是展示"为什么这样设计、代价是什么"。这一节给出四步骨架、六道高频题与项目案例模板。

读完这一节,你能做到

  1. 用四步结构回答原理题。
  2. 把值/引用、async、GC、EF Core、DI、中间件六题讲到点上。
  3. 用 STAR 讲清一次工程决策与代价。
  4. 应对"如果重来/为什么不用 Java"这类追问。

一、四步表达骨架

结论(一句话判断)
→ 机制(底层怎么实现)
→ 取舍(为什么这样设计,代价是什么)
→ 验证(怎么证明)

示例:问"async/await 是开线程吗"。

  • 结论:不是。它是状态机,IO 未完成时让出线程。
  • 机制:编译成状态机,await 注册回调,完成后恢复。
  • 取舍:适合 IO;CPU 密集要 Task.Run,否则占用线程池。
  • 验证:对比同步阻塞与异步的线程数、吞吐。

二、六道高频题

1. 值类型与引用类型

"值类型存值、赋值拷贝;引用类型存引用、共享对象。struct 适合小且不可变的值,class 适合有身份与继承。装箱会分配堆对象,泛型可以避免。"

2. async/await

"状态机 + 回调;await 未完成时让出线程;不要用 .Result 阻塞;类库用 ConfigureAwait(false);取消用 CancellationToken。"

3. GC

"分代回收,Gen0 频繁、Gen2 少;LOH 大于 85KB 且默认不压缩;GC 只管托管内存,非托管资源要 IDisposable;先看 counters 再抓 dump。"

4. EF Core

"IQueryable 延迟执行,翻译成 SQL;N+1 用 Include/投影/批量;只读用 AsNoTracking;批量用 ExecuteUpdate;并发用 RowVersion。"

5. DI 生命周期

"Singleton 进程内唯一、Scoped 每请求、Transient 每次解析;Singleton 捕获 Scoped 会串数据,用 ScopeFactory 解决。"

6. 中间件

"洋葱模型,Use 可 next、Run 终止、Map 分支;顺序决定鉴权与路由是否生效;中间件是单例,Scoped 服务在 InvokeAsync 注入。"

三、项目案例模板

S:业务规模、QPS、数据量、团队
T:要解决的问题(慢、抖、贵、难维护)
A:方案与取舍(为什么是它,排除了什么)
R:指标变化 + 代价

示例:订单接口 P99 1.2s,排查为 N+1 + 无索引。方案:投影 + 联合索引 + 输出缓存;代价是缓存一致性需要处理。结果 P99 220ms,数据库 QPS 降 55%。

不要只讲"用了 Dapper/微服务",要讲约束、方案、代价与验证。

四、追问应对

追问回答骨架
为什么不用 Java?团队栈、生态、成本,讲取舍不拉踩
如果数据量再大 10 倍?分库分表/读写分离/缓存/异步,先算瓶颈
这个优化真的有效吗?基线、单变量、前后对比
失败过什么?假设 → 动作 → 结果 → 复盘
看过源码吗?讲一个具体类(如 TaskAwaiter、DbContext),不要泛泛而谈

五、常见坑

  1. 只背 API,讲不出机制。
  2. 把 async 说成"多线程"。
  3. 认为 GC 会自动释放所有资源。
  4. 项目案例没有指标与代价。
  5. 遇到不会的硬编,不如说"这块我没深挖,我的理解是……"。

六、动手任务:自写 → AI 写 → 对比 → 取舍

四步不跳:① 自己写(限时,关掉补全)→ ② 让 AI 写同一题 → ③ 逐条比对 → ④ 说出保留谁的写法。 完整动作与记录模板见《AI 协作与代码评审》。

① 自己写

选一个你做过的接口,用四步结构讲清一次优化,要求:

  • 至少两个指标的前后对比。
  • 至少一个代价或没做好的地方。
  • 结尾给出"如果重做会怎么改"。

② 让 AI 写同一题

把同一道题交给 AI:用三分钟讲清一次 async/await 引发的线程池饥饿,要求现象、假设、验证、结论。

③ 对比要盯的点

  • 它有具体的线程池指标吗?还是“性能变差了”。
  • 机制讲到哪一层?停在“async 就是异步”还是能说到状态机与线程归还。
  • 它有没有讲代价?比如全链路异步化带来的复杂度。

④ 一句话取舍

它哪一句是正确的空话,一追问就塌?

七、自测

  1. 四步表达骨架是什么?
  2. async/await 编译后是什么?
  3. GC 分代与 LOH 的特点?
  4. DI 三种生命周期的陷阱?
  5. 中间件的执行顺序为什么重要?

参考答案要点:结论/机制/取舍/验证;状态机;Gen0 频繁、LOH 不压缩;Singleton 捕获 Scoped;短路与依赖。

附录:高频追问

问:为什么 .NET 面试爱问 async? 它能同时考线程、状态机、异常与性能取舍。

问:Task 和 ValueTask 怎么选? 高频且常同步完成用 ValueTask;否则用 Task。

问:record 和 class 的区别? record 有值相等与 with,适合不可变模型;class 有引用身份。

问:Span 能跨 await 吗? 不能,ref struct 限制;跨异步用 Memory。

问:EF Core 和 Dapper 怎么选? 复杂模型用 EF Core,高性能与 SQL 可控用 Dapper,可混用。

问:如何做性能优化? 先测量:接口耗时、SQL、GC、线程池;再按瓶颈优化,最后对比指标。

问:如何回答"如果重来"? 讲你会先验证什么、会避免哪个坑,体现复盘能力。

问:面试官问源码怎么答? 选一个你真读过的类,讲设计意图、关键方法、边界,不要求全。

问:反问环节问什么? 团队最大技术痛点、岗位前三个月目标;不要问薪资加班。

问:怎么证明方案有效? 基线、单变量、前后对比;没有数据就讲判断过程。

附:面试节奏与准备清单

原理题 2-3 分钟:结论 → 机制 → 一句取舍。项目题 3-5 分钟:背景一句、问题一句、动作三句、结果一句、反思一句。

  • 一条主线能讲 3 分钟
  • 六道高频题各能讲 2 分钟
  • 一个性能案例(指标 + 代价)
  • 一个失败案例(假设 + 复盘)
  • 两个反问问题

三个不要

不要背 API 不讲机制、不要把团队成果全归因于自己、不要讲没有数据的“大幅提升”。讲清判断与代价,比数字更重要。

准备好两个失败案例,往往比十个成功案例更能证明水平。

补充:高频追问速答

问:async 一定比 sync 快吗? 不一定。它提升吞吐,单次延迟可能更高。

问:GC 会自动释放连接吗? 不会,连接要实现 Dispose 并用 using。

问:EF Core 的 AsNoTracking 什么时候不能用? 需要更新实体时。

问:DI 的 Scoped 在后台线程怎么办? 用 IServiceScopeFactory 创建作用域。

问:中间件能短路吗? 能,不调用 next 就短路。

补充:反问清单

  1. 团队当前最大的技术痛点是什么?
  2. 这个岗位前三个月最重要的目标是什么?

收尾用一句话总结“我能解决什么问题”,并给对方留出追问空间。不要问薪资、加班、能不能远程。

收尾话术

“我今天的回答如果有不清楚的地方,可以随时追问我补充。”给对方留出继续深挖的空间,也给自己留出修正的机会。面试是双向选择,保持平等心态。面试官更在意你的判断过程,而不是标准答案本身。结论先行,细节随后;讲判断,别背。先结论,后机制。

补遗:常用设计模式在 .NET 的落点

单例(DI Singleton)、工厂(IServiceProvider)、策略(按 key 解析服务)、模板方法(抽象基类)、责任链(中间件/DelegatingHandler)、代理(DispatchProxy/Castle)、观察者(event/IObservable)、建造者(配置构建)。

讲模式要讲“变化点”:策略替换算法、责任链调整顺序、代理加横切逻辑。

配套题目

★ 越多越该先背;点题目可看完整答案与深入版。

提示:读完可以顺手做几道对应题目,检验是否真的理解