
CountBy、AggregateBy、Base64Url、Task.WhenEach、Lock 类型——.NET 9 标准库带来 20 实用 API版本定位适用版本.NET 9 前置知识LINQ 基础、集合操作背景.NET 9 不只是 C# 语言的更新标准库也带来了一大批实用的新 API。从 LINQ 的CountBy/AggregateBy到安全领域的BinaryFormatter移除从高性能的SearchValues增强到并发编程的Task.WhenEach这些 API 解决了长期以来的痛点让代码更简洁、性能更好、安全性更强。新增 API 一览API命名空间用途实用性CountBySystem.Linq按键计数无需 GroupBy⭐⭐⭐⭐⭐AggregateBySystem.Linq按键聚合无需 GroupBy⭐⭐⭐⭐⭐IndexSystem.Linq为元素添加索引⭐⭐⭐⭐Base64UrlSystem.BuffersURL 安全的 Base64 编解码⭐⭐⭐⭐⭐Task.WhenEachSystem.Threading.Tasks按完成顺序获取 Task 结果⭐⭐⭐⭐⭐BinaryFormatter 移除—安全完全移除危险的序列化器⭐⭐⭐⭐⭐Lock 类型System.Threading新的轻量级线程同步原语⭐⭐⭐⭐OrderedDictionaryTKey,TValueSystem.Collections.Generic有序字典⭐⭐⭐⭐Guid v7System时间有序的 GUID⭐⭐⭐⭐ServerSentEventsSystem.Net.ServerSentEventsSSE 编解码⭐⭐⭐⭐SearchValues 增强System.Buffers更高性能的搜索⭐⭐⭐⭐FrozenDictionary 增强System.Collections.Frozen批量添加、TryCreateFrozen⭐⭐⭐⭐TensorTSystem.Numerics.TensorsAI 计算基块⭐⭐⭐PriorityQueue.RemoveSystem.Collections.Generic从队列中移除元素⭐⭐⭐⭐Parallel.ForEachAsyncSystem.Threading.Tasks异步并行ForEach⭐⭐⭐⭐Span/ReadOnlySpan 改进System隐式转换、新方法⭐⭐⭐⭐System.Text.Json 改进System.Text.Json枚举值字符串、UnmappedMemberHandling⭐⭐⭐⭐CryptographicOperationsSystem.Security.Cryptography一次性哈希/验证⭐⭐⭐⭐StringSyntax 增强System.Diagnostics.CodeAnalysis更多语法标记⭐⭐⭐Polyfill 支持System.Runtime.CompilerServices内置 Polyfill⭐⭐⭐TimeSpan 从整数创建System避免浮点精度问题⭐⭐⭐PersistedAssemblyBuilderSystem.Reflection.Emit可保存的动态程序集⭐⭐⭐Stopwatch 改进System.DiagnosticsGetTimestamp 高精度计时⭐⭐⭐一、LINQ 新方法详解1. CountBy —— 按键计数之前的做法GroupBy Count// .NET 8 及之前 var counts list .GroupBy(x x.Category) .Select(g new { Category g.Key, Count g.Count() }) .ToDictionary(x x.Category, x x.Count); // { A: 3, B: 2, C: 1 }.NET 9 的做法一行搞定var counts list.CountBy(x x.Category); // { A: 3, B: 2, C: 1 }对比GroupBy CountCountBy代码行数3-4 行1 行中间分配多次无返回类型DictionaryIEnumerableKeyValuePair2. AggregateBy —— 按键聚合// 旧做法 var totals list .GroupBy(x x.Type) .Select(g new { Type g.Key, Total g.Sum(x x.Amount) }); // .NET 9 var totals list.AggregateBy( keySelector: x x.Type, seed: 0m, accumulator: (acc, item) acc item.Amount );更复杂的聚合var stats list.AggregateBy( keySelector: x x.Category, seed: new { Total 0m, Count 0 }, accumulator: (acc, item) new { Total acc.Total item.Amount, Count acc.Count 1 } );3. Index —— 添加索引// 旧做法 var indexed list.Select((item, index) new { Index index, Item item }); // .NET 9 var indexed list.Index(); foreach (var (index, item) in indexed) { Console.WriteLine(${index}: {item}); }二、高性能 API4. Base64Url 编解码痛点URL 中不能直接用标准 Base64、/、有特殊含义// 旧做法手动替换 string ToBase64Url(byte[] data) { return Convert.ToBase64String(data) .TrimEnd() .Replace(, -) .Replace(/, _); } // .NET 9内置支持 string encoded Base64Url.EncodeToString(data); byte[] decoded Base64Url.FromBase64String(encoded); // Span 版本零分配 int written Base64Url.EncodeToUtf8(data, buffer);典型场景JWT token、URL 安全的 ID、加密数据传输5. SearchValues 增强// .NET 8只支持字符和字节 var searchValues SearchValues.Create(abc); // .NET 9支持更多类型 var intSearch SearchValues.Create(new[] { 1, 2, 3 }); var utf8Search SearchValues.Create(hellou8); // 使用 ReadOnlySpanint data [1, 2, 3, 4, 5, 1, 2, 3]; int index data.IndexOfAny(intSearch); // 返回 06. FrozenDictionary / FrozenSet 增强// .NET 8只能从 Dictionary 创建 var frozen new Dictionarystring, int { [a] 1, [b] 2 } .ToFrozenDictionary(); // .NET 9更多创建方式 var frozen2 FrozenDictionary.ToFrozenDictionary( [(a, 1), (b, 2)] ); // TryCreateFrozen批量添加 var result FrozenDictionary.TryCreateFrozen( [(a, 1), (b, 2), (a, 3)], // 重复键 EqualityComparerstring.Default, out var frozen3 // 只保留第一个 a );7. Span / ReadOnlySpan 改进// .NET 9 新增的隐式转换 Spanint span new int[] { 1, 2, 3 }; // 数组到 Span 隐式转换 // 新增方法 ReadOnlySpanchar span Hello, World!; span.ContainsAny(aeiou); // 检查是否包含任一字符 span.ContainsAny(aeiou.AsSpan());三、并发与异步8. Task.WhenEach —— 按完成顺序痛点Task.WhenAll等所有完成才返回无法按完成顺序处理// 旧做法 var results await Task.WhenAll(task1, task2, task3); // 全部完成后才能处理 // .NET 9按完成顺序逐个处理 await foreach (var task in Task.WhenEach(task1, task2, task3)) { var result await task; Console.WriteLine($完成: {result}); // 第一个完成的任务立即处理不等其他 }典型场景并发 HTTP 请求、批量任务处理、实时日志收集9. Parallel.ForEachAsync 改进// .NET 8 await Parallel.ForEachAsync(items, new ParallelOptions { MaxDegreeOfParallelism 4 }, async (item, ct) { await ProcessAsync(item, ct); }); // .NET 9更简洁 await Parallel.ForEachAsync(items, async (item, ct) { await ProcessAsync(item, ct); }); // 自动选择最优并行度10. Lock 类型痛点lock语句使用 Monitor无法跨 await 使用// .NET 8 及之前 private readonly object _lock new object(); lock (_lock) { // 不能 await } // .NET 9新的 Lock 类型 private readonly Lock _lock new Lock(); // 在同步代码中使用 lock (_lock) { _counter; } // 在异步代码中使用 using (await _lock.EnterAsync()) { await UpdateAsync(); }四、集合改进11. OrderedDictionaryTKey, TValue// .NET 8没有内置的有序字典 // 需要自己实现或用第三方库 // .NET 9 var dict new OrderedDictionarystring, int(); dict.Add(first, 1); dict.Add(second, 2); dict.Add(third, 3); // 保持插入顺序 foreach (var kvp in dict) { Console.WriteLine(${kvp.Key}: {kvp.Value}); } // 输出顺序first, second, third // 按索引访问 var item dict[0]; // first: 112. PriorityQueue.Removevar queue new PriorityQueuestring, int(); queue.Enqueue(low, 10); queue.Enqueue(high, 1); queue.Enqueue(medium, 5); // 移除元素 bool removed queue.Remove(high, out string? element, out int priority); // removed true, element high, priority 113. Guid v7时间有序// .NET 8生成随机 GUID var guid1 Guid.NewGuid(); // 完全随机 // .NET 9生成时间有序 GUID var guid2 Guid.CreateVersion7(); // 基于时间排序适合作为数据库主键 // 从时间戳创建 var guid3 Guid.CreateVersion7(DateTimeOffset.UtcNow);优势数据库索引友好、分布式系统中保持排序、兼容 v1/v4 格式五、安全改进14. BinaryFormatter 完全移除 ⚠️这是 .NET 9 最重要的安全改进之一// .NET 8已标记为 obsolete // [Obsolete] BinaryFormatter // .NET 9完全移除无法使用 // var formatter new BinaryFormatter(); // 编译错误为什么移除BinaryFormatter 存在严重的反序列化漏洞已被大量 CVE 利用替代方案场景推荐方案JSON 序列化System.Text.Json二进制序列化protobuf-net高性能序列化MessagePack.NET 内部序列化ISerializable 自定义实现迁移示例// 旧代码 using var formatter new BinaryFormatter(); using var stream new FileStream(data.bin, FileMode.Open); var data formatter.Deserialize(stream); // 新代码 using var stream new FileStream(data.json, FileMode.Open); var data await JsonSerializer.DeserializeAsyncMyData(stream);15. CETControl-flow Enforcement Technology影子栈.NET 9 在支持 CET 的硬件上自动启用影子栈保护防止 ROPReturn-Oriented Programming攻击对应用透明无需代码修改16. CryptographicOperations 改进// 一次性哈希更安全避免复用 byte[] hash CryptographicOperations.HashData( HashAlgorithmName.SHA256, data ); // 一次性验证 bool valid CryptographicOperations.VerifyHash( HashAlgorithmName.SHA256, data, signature );六、其他重要 API17. ServerSentEvents 编解码// 旧做法手动构建 SSE 格式 await response.WriteAsync(data: hello\n\n); // .NET 9内置 SSE 支持 using var writer new SseWriter(response.Body); await writer.WriteAsync(new SseMessage { EventType message, Data hello });18. System.Text.Json 改进// 枚举值字符串无额外分配 var options new JsonSerializerOptions { Converters { new JsonStringEnumConverter() } }; // UnmappedMemberHandling严格模式 var strictOptions new JsonSerializerOptions { UnmappedMemberHandling JsonUnmappedMemberHandling.Disallow }; // 遇到未映射的 JSON 属性时抛出异常19. TensorTAI 计算基块// 用于 AI/ML 计算的基础张量类型 var tensor Tensor.Createfloat(new[] { 2, 3 }); // 2x3 矩阵 tensor[0, 0] 1.0f; tensor[0, 1] 2.0f; // 数学运算 var result tensor tensor; var multiplied tensor * 2.0f;20. TimeSpan 从整数创建// .NET 8只支持 double var ts TimeSpan.FromSeconds(30.0); // .NET 9支持 int避免浮点精度问题 var ts2 TimeSpan.FromSeconds(30); // int 重载 var ts3 TimeSpan.FromMilliseconds(500); var ts4 TimeSpan.FromTicks(10000);21. StringSyntax 增强// .NET 9 扩展了 StringSyntax 属性 public class MyOptions { [StringSyntax(StringSyntaxAttribute.Regex)] public string? Pattern { get; set; } [StringSyntax(StringSyntaxAttribute.Json)] public string? Json { get; set; } [StringSyntax(StringSyntaxAttribute.DateOnlyFormat)] public string? DateFormat { get; set; } [StringSyntax(StringSyntaxAttribute.TimeOnlyFormat)] public string? TimeFormat { get; set; } }实战场景CountBy AggregateBy 组合// 电商统计每个类别的订单数和总金额 var stats orders .CountBy(o o.Category) .ToDictionary(x x.Key, x x.Value); var totals orders .AggregateBy( o o.Category, 0m, (total, order) total order.Amount );Task.WhenEach 实时处理// 并发下载多个文件按完成顺序保存 var downloads urls.Select(url HttpClient.GetAsync(url)); await foreach (var task in Task.WhenEach(downloads)) { var response await task; var fileName ExtractFileName(response.RequestMessage.RequestUri); await SaveFileAsync(fileName, response.Content); Console.WriteLine($已保存: {fileName}); }Lock 替代 lock 语句public class ThreadSafeCacheTKey, TValue { private readonly Lock _lock new(); private readonly DictionaryTKey, TValue _cache new(); public async TaskTValue GetOrAddAsync(TKey key, FuncTaskTValue factory) { using (await _lock.EnterAsync()) { if (_cache.TryGetValue(key, out var value)) return value; value await factory(); _cache[key] value; return value; } } }BinaryFormatter 迁移// 旧BinaryFormatter FileStream // 新System.Text.Json FileStream public static async Task SaveDataAsyncT(string path, T data) { var options new JsonSerializerOptions { WriteIndented true }; await using var stream File.Create(path); await JsonSerializer.SerializeAsync(stream, data, options); } public static async TaskT? LoadDataAsyncT(string path) { await using var stream File.OpenRead(path); return await JsonSerializer.DeserializeAsyncT(stream); }迁移建议从 GroupBy 迁移到 CountBy/AggregateBy// 旧代码 var counts list.GroupBy(x x.Key).ToDictionary(g g.Key, g g.Count()); // 新代码 var counts list.CountBy(x x.Key).ToDictionary(x x.Key, x x.Value); // 注意CountBy 返回 IEnumerableKeyValuePair如需 Dictionary 需 ToDictionaryBinaryFormatter 迁移检查清单全局搜索BinaryFormatter、FormatterAssemblyStyle替换为System.Text.Json或protobuf-net测试所有序列化/反序列化路径验证数据兼容性注意事项API注意点CountBy返回IEnumerableKeyValuePair不是DictionaryTask.WhenEach需要 .NET 9不支持 netstandardLock不能替代 Monitor 的所有用法如 TryEnterGuid v7需要系统时钟不保证全局唯一BinaryFormatter.NET 9 完全移除必须迁移一句话总结.NET 9 标准库带来 20 实用 APILINQ 更简洁、并发更灵活、安全更可靠、性能更出色。官方文档Whats new in .NET 9 librariesLINQ improvementsPriorityQueue.RemoveBase64UrlTask.WhenEachLockBinaryFormatter removal