模块六 · AI 协作与表达
面试表达:把 .NET 讲成工程取舍
.NET 面试不是背 API,而是展示"为什么这样设计、代价是什么"。这一节给出四步骨架、六道高频题与项目案例模板。
读完这一节,你能做到
- 用四步结构回答原理题。
- 把值/引用、async、GC、EF Core、DI、中间件六题讲到点上。
- 用 STAR 讲清一次工程决策与代价。
- 应对"如果重来/为什么不用 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),不要泛泛而谈 |
五、常见坑
- 只背 API,讲不出机制。
- 把 async 说成"多线程"。
- 认为 GC 会自动释放所有资源。
- 项目案例没有指标与代价。
- 遇到不会的硬编,不如说"这块我没深挖,我的理解是……"。
六、动手任务:自写 → AI 写 → 对比 → 取舍
四步不跳:① 自己写(限时,关掉补全)→ ② 让 AI 写同一题 → ③ 逐条比对 → ④ 说出保留谁的写法。 完整动作与记录模板见《AI 协作与代码评审》。
① 自己写
选一个你做过的接口,用四步结构讲清一次优化,要求:
- 至少两个指标的前后对比。
- 至少一个代价或没做好的地方。
- 结尾给出"如果重做会怎么改"。
② 让 AI 写同一题
把同一道题交给 AI:用三分钟讲清一次 async/await 引发的线程池饥饿,要求现象、假设、验证、结论。
③ 对比要盯的点
- 它有具体的线程池指标吗?还是“性能变差了”。
- 机制讲到哪一层?停在“async 就是异步”还是能说到状态机与线程归还。
- 它有没有讲代价?比如全链路异步化带来的复杂度。
④ 一句话取舍
它哪一句是正确的空话,一追问就塌?
七、自测
- 四步表达骨架是什么?
- async/await 编译后是什么?
- GC 分代与 LOH 的特点?
- DI 三种生命周期的陷阱?
- 中间件的执行顺序为什么重要?
参考答案要点:结论/机制/取舍/验证;状态机;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 就短路。
补充:反问清单
- 团队当前最大的技术痛点是什么?
- 这个岗位前三个月最重要的目标是什么?
收尾用一句话总结“我能解决什么问题”,并给对方留出追问空间。不要问薪资、加班、能不能远程。
收尾话术
“我今天的回答如果有不清楚的地方,可以随时追问我补充。”给对方留出继续深挖的空间,也给自己留出修正的机会。面试是双向选择,保持平等心态。面试官更在意你的判断过程,而不是标准答案本身。结论先行,细节随后;讲判断,别背。先结论,后机制。
补遗:常用设计模式在 .NET 的落点
单例(DI Singleton)、工厂(IServiceProvider)、策略(按 key 解析服务)、模板方法(抽象基类)、责任链(中间件/DelegatingHandler)、代理(DispatchProxy/Castle)、观察者(event/IObservable)、建造者(配置构建)。
讲模式要讲“变化点”:策略替换算法、责任链调整顺序、代理加横切逻辑。
配套题目
★ 越多越该先背;点题目可看完整答案与深入版。
- ★★★★★ 一个支付回调接口怎么做幂等?
- ★★★★☆ 高并发下的库存扣减怎么设计才不超卖?
- ★★★☆☆ 跨服务的数据一致性怎么保证?Outbox 模式解决什么问题?
- ★★★☆☆ 线上 .NET 服务变慢或内存涨,你会用哪些工具排查?
- ★★★★★ 值类型和引用类型有什么区别?struct 和 class 怎么选?
提示:读完可以顺手做几道对应题目,检验是否真的理解