今天这篇文章主要分享下关于.NET 旧项目与.NET 10的关键性差异写法,起因是上周一个老同事问我他们公司的项目应该怎么与AI融合升级,当打开项目的时候发现还是用的.NET 6,并不是对旧项目存在技术歧视,在AI Coding的时代应该好好运用这个能力早日将技术负债干掉,追赶新一轮的软件行业洗牌。
「.NET 6 之前的经典写法」: 「.NET 6+ 的现代写法」(Top-level statements + Minimal Hosting): 「为什么换」:少了一个文件、少了一层抽象、配置和路由都在一个地方一眼看懂。新项目模板从 .NET 6 开始就是这个样子,「还在用 Startup 的等于在告诉所有人"我没跟上时代"」。 「.NET 6 时代很多人这么写」: 看着挺合理对吧?「但这是错的」。 「正确的写法」: 「为什么换」: 第一,字符串插值在日志级别被禁用时仍然会执行——你设置 LogLevel 为 Warning,那行 Information 日志虽然不输出,但字符串拼接的开销还在。结构化模板则是延迟执行。 第二,「结构化日志的字段可以被搜索」。在 Seq、Elasticsearch、Application Insights 里,你可以直接 .NET 9 开始更狠——直接给你提供了 「LoggerMessage 源生成器」,性能比传统方式还快好几倍: 编译时生成代码,零反射、零装箱、零字符串拼接。能用这个就用这个。 「老写法」: 「.NET 9+ 推荐」: 「为什么换」: .NET 9 引入了正式的 这是那种"知道了就立刻全项目替换"的零成本升级。 「老写法」: 「C# 13 / .NET 9 起的新写法」( 「为什么换」: C# 团队等了将近 10 年才把 注意: 「老写法」: 「C# 13+ 新写法」: 「为什么换」: 老写法每次调用都会在堆上分配一个数组,对高频调用方法是隐性的性能杀手。新写法编译器直接把字面量参数放在栈上,「零堆分配、零 GC 压力」。 .NET 9 BCL 已经给 606 个方法补充了 「老写法」: 「.NET 9 新写法」: 类似的还有 「为什么换」: LINQ 不是一成不变的"老 API",每个版本都在补漏。这三个方法解决的是过去「人人都手写过 N 次、又每次都觉得别扭」的场景。代码更短、意图更清晰,性能也更好(少了中间集合分配)。 「.NET 8 之前的老写法」: 「.NET 9 新写法」: 「为什么换」: 老写法不仅难看, 批量调用外部 API、并发查多个数据源的场景,用这个能让首结果处理延迟从"最慢请求"缩短到"最快请求"——业务感知会非常明显。 「老写法」: 「EF Core 7+ 起的批量 API」(在 EF Core 9/10 更完善): 类似的还有 「为什么换」: 旧写法本质上是 EF Core 在帮你「模拟批量操作」,每条数据都要往返一次内存、做变更追踪。新 API 直接生成一条原生 SQL,性能完全是两个量级。大数据量场景这个差异是用户能感知的。 「.NET 7 之前的经典写法」: 「.NET 8+ 推荐」(基于 「为什么换」: Polly v8 重构了 API,从"策略对象"模型升级到了"管道"模型。 老写法不算"错",但比起新 API 来说,「繁琐且容易配错」。 「.NET 9 之前的"标准"写法」: 「.NET 9+ 推荐」( 「为什么换」: 它还自动管理 L1(内存)+ L2(Redis 等分布式)两级缓存:本地有就用本地,没有去 Redis 取,再没有调用 factory。「老式手写双检锁的所有理由都不复存在了」。 技术债不可怕,可怕的是不知道自己背了多少。这篇文章如果让你看到了几条"我也是这么写的",那它就值了。 最后给一个务实建议:「不要为了"换新写法"专门搞一个 PR」,应该是下次动到那个文件时顺手改了。这种渐进式的现代化,比一次大改靠谱得多。1.
Startup.cs2.
Console.WriteLine($"用户 {userId} 登录") 这种日志写法UserId = 123 查出某个用户的所有日志。字符串插值出来的日志,永远只能用关键词模糊搜,效率天壤之别。3.
lock(new object()) 已经不是最佳实践了System.Threading.Lock 类型,「底层比传统 Monitor 更高效」,调试器中可视性也更好。你的代码改动只有"一行类型声明",业务逻辑零修改,免费拿到性能提升和更清晰的语义。4. 手写 backing field 做属性校验,已经没必要了
field 关键字):field 关键字加进来——因为太多人有名为 field 的变量,向后兼容要慎重。但加进来之后,「所有"setter 里要做点事但又懒得手写 backing field"的场景」都被它解决了。field 关键字在 C# 14(.NET 10)正式发布,之前的版本需要 <LangVersion>preview</LangVersion> 才能用。5.
params object[] args 该换成 params ReadOnlySpan<T> 了ReadOnlySpan 版本的 params 重载——你重新编译一遍现有项目,编译器会自动选最优重载,「业务代码一行不改,免费拿到性能提升」。6.
IEnumerable<T>.GroupBy().Select(g => new { Key, Count }) 该精简了AggregateBy(任意聚合)、Index(带下标遍历):7. 用
Task.WhenAny 手写"先到先得"循环?过时了tasks.Remove() 是 O(n) 操作,任务多了性能会衰减。Task.WhenEach 直接返回一个 IAsyncEnumerable<Task<T>>,「按完成顺序产生任务」,配合 await foreach 极度优雅。8. EF Core 改完数据再
SaveChanges,大数据场景该用批量 API 了ExecuteDeleteAsync——删除大量记录时不要再 db.Remove + SaveChanges 了。9. 配置 HttpClient 的 Polly 重试策略,写法变了
Microsoft.Extensions.Http.Resilience):AddStandardResilienceHandler() 直接给你一套「生产级默认配置」——重试 + 熔断 + 超时 + 限流 + Hedging 全都安排好了,不用再像以前那样手动一个一个组合。10.
AddDistributedMemoryCache + 自己写双重检查锁?.NET 9 一行替代HybridCache):HybridCache 是 .NET 9 新增的分层缓存抽象,「内置防 Cache Stampede(缓存踩踏)机制」——100 个并发请求同时打过来,只会有 1 个真正去查数据库,剩下的 99 个等待结果。最后