为了账号安全,请及时绑定邮箱和手机立即绑定

C# 13和.NET 9必知的新特性

标签:
C# .NET

.NET 9就要来了,将带来许多变化和改进。这篇文章将带你了解.NET 9 和 C# 13 中的最具影响力和最常用的功能。

1. 锁实例

C# 13 引入了一种新的 System.Threading.Lock 类型,用于实现互斥。过去,通常使用 object 类型进行加锁,而现在有了专门的 Lock 类型,预计将成为大多数互斥场景中的标准选择。

  • 更干净、更安全的代码。 它使代码更易读和更可预测。此外,编译器会在 Lock 实例被错误地用作常规 object 时发出警告。
  • 性能。 根据微软的说法,它比在任意 object 实例上加锁更高效。
  • 新的加锁机制。 EnterScope 在底层替代了 Monitor 类。你可以在这里找到上面示例的 IL 代码。它返回一个遵循 Dispose 模式的 ref struct,可以无缝地与 using 语句一起使用。
  • 异步限制。 由于锁和异步代码交互的固有局限性,在锁块中仍然不允许进行异步调用。传统上,SemaphoreSlim 是首选的替代方案。
2. Task.WhenEach

想象你有一份任务清单,每个任务完成的时间各不相同。你希望每个任务一完成后就立即处理。WaitAll() 不适用于这种情况,因为它会等待所有任务全部完成。我们可以使用 Task.WaitAny() 作为替代手段来处理下一个完成的任务。C# 10 引入了一种更优雅和高效的方式来处理这种情况:Task.WhenEach。下面是一个示例。

Task.WhenEach 返回 IAsyncEnumerable<Task<TResult>>,并且可以利用 await foreach 轻松地遍历随着它们的完成的每个任务👌

3 参数集

从 C# 13 版本开始,params 参数可以是任何可以在集合表达式中使用的类型。

  • 更清晰的代码。 显著减少了对.ToArray().ToList()的调用次数。
  • 性能。 调用.ToArray().ToList()本身就会增加额外的资源开销。此外,现在支持传递Span<>IEnumerable<>,利用更高效的内存使用和惰性执行。总体而言,在需要更好性能的场景下,这提供了更大的灵活性和更好的性能表现 🚀。
4. 半自动特性

当你在 C# 中声明一个自动实现的属性,比如 public int Number { get; set; } 时,编译器会生成一个字段(例如 _number)和内部的 getter 和 setter 方法,比如 void set_Number(int number)int get_Number()。但是,如果你需要自定义逻辑,比如验证、默认值、计算或延迟加载等,通常需要手动在类中定义字段。C# 13 通过引入 field 关键字简化了这个过程,允许你直接访问字段而无需手动定义它。

  • 减少样板代码。 消除了手动定义私有字段的需要,从而产生更干净、更简洁的代码效果。
  • 增强可读性。 无需管理自定义的后备字段名称,使使用字段关键字成为标准做法,从而提高代码的清晰度。
  • 属性范围内的字段。 私有后备字段仅限于属性本身,防止在类的其他部分意外使用。这种封装提高了安全性,并解决了常见的错误来源。
  • 🚨潜在的破坏性变更。 如果您的类已经有一个名为 field 且类型相同的属性,它将优先于新的 field 关键字,可能导致意外行为或错误。您可以在此处查看C# 团队的提案关于如何处理此问题的建议。这可能是自 2016 年首次提出该功能以来一直被推迟的原因之一,也是该功能迟迟未能发布的因素之一。
5. 混合缓存机制

新的 HybridCache API 解决了现有 IDistributedCacheIMemoryCache API 中的一些不足,如竞态条件,并引入了新的功能和能力,使 .NET 中的缓存更加灵活且性能更高。HybridCache 被设计为大多数 IDistributedCacheIMemoryCache 场景的即插即用替代品。

  • 两全其美。 HybridCache 允许您使用统一、简洁的 API 将数据存储在内存(L1)或分布式(L2)缓存中。这种配置允许您快速访问常用的本地数据(L1),同时提供一个可扩展的外部缓存(L2)来处理较大的、不常访问的数据。[HybridCacheEntryFlags](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.caching.hybrid.hybridcacheentryflags?view=netframework-4.8.1) 用于控制这种行为。
  • 防止踩踏效应。 IMemoryCacheIDistributedCache 都存在踩踏问题。HybridCache 通过确保只有一个请求者重新生成给定键的值,而其他请求者等待结果,来解决这个问题。这有助于避免缓存过度刷新。
  • 额外功能。 HybridCache 提供了诸如标记、可配置的序列化 通过使用 .WithSerializer(...).WithSerializerFactory(...) 方法,以及通过使用 [ImmutableObject(true)] 注解的 缓存实例复用 等额外功能。

💡乍一听可能有点复杂,但要让 HybridCache 将数据存储在进程外部(L2),你仍然需要配置一个 IDistributedCache(例如 Redis),HybridCache 会依赖于它。即使没有 IDistributedCacheHybridCache 仍然会提供进程内的缓存。在我上面提到的情况下,我有如下配置:

6. 内置的 OpenAPI 文档生成功能

从 .NET 5 版本开始,Web API 模板自带了对 OpenApi 规范的内置支持,通过 [Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) 包。在 .NET 9 中,微软继续通过其内部开发的 [Microsoft.AspNetCore.OpenApi](https://github.com/dotnet/aspnetcore/tree/main/src/OpenApi) 包支持 OpenApi 规范,并且该包将取代 [Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) 包。

运行你的应用之后,前往 /openapi/v1.json 页面查看生成的 OpenApi 文档内容。

  • Swagger UI. 语法变得更简洁,乍一看更像‘原生’。但是,默认情况下,你只能得到一个 OpenAPI 文档,并没有包含交互式的 API 文档😢。如果你需要像 Swagger UI 这样的交互式 API 文档,你可能需要集成第三方工具,例如 [Scalar](https://github.com/scalar/scalar) 来实现。这里有一份详细的指南:Scalar .NET API Reference Integration
  • 构建时间生成。 你可以使用 [Microsoft.Extensions.ApiDescription.Server](https://github.com/dotnet/aspnetcore/tree/main/src/Tools/Extensions.ApiDescription.Server) 包在构建时生成 OpenAPI 文档。
7. 搜索值优化.

SearchValues 在 .NET 8 中引入,提供了一个不可变的、只读的值类,与传统的 ICollection.Contains 方法相比,它在搜索效率上有显著提升。最初,它仅限于字符或字节类型。在 .NET 9 中,SearchValues<T> 也支持字符串类型。

你现在也可以通过 StringComparison 参数来指定比较类型。

将来,这将成为需要进行大量文本处理的应用程序的首选,例如文档解析、输入过滤、垃圾邮件检测、数据脱敏和搜索等。

8. 新的 LINQ 方法(或功能),

确保“LINQ”未被翻译,保持英文原样。此处已按专家建议调整为更口语化且更具清晰度的表达方式,同时添加了逗号以匹配源文本的风格。

新加入了三种方法:CountByAggregateByIndex,它们旨在提高处理常见数据操作任务的效率和简洁性。下面的示例将展示这些方法的具体用法。

计数

按聚合

目录

我最喜欢的 Index()💛 方法是因为 foreach 没有索引,这一直以来都让人很烦恼,并且常常导致使用更复杂的替代方案。

8. 自动生成 UUID v7

自从.NET早期以来,我们就使用Guid.NewGuid()来生成UUID。此方法生成的是版本4的UUID。自那以后,UUID规范有了显著的进展,目前的稳定版本是7。版本7的一个关键特性是包含在UUID中的时间戳。它的结构如下:

    +------------------+---------------+----------------------+
    | 48位时间戳字段   | 12位随机数    | 62位另一组随机数     |
    +------------------+---------------+----------------------+

说明:上表展示了字段的位数分配,其中48位用于时间戳字段,12位用于随机数,62位用于另一组随机数。

时间戳部分带来了重要的好处:可以根据其创建时间对UUID进行排序。这使得它们更适合作为数据库中的标识符,并在分布式环境中提供了更好的唯一性保证。

现在 .NET 已经内置了 UUID v7 的生成,因此你不再需要使用外部库如 [UUIDNext](https://github.com/mareek/UUIDNext) 来实现这一功能。引入了一个新的方法 Guid.CreateVersion7(),此方法还可以接受一个时间戳参数,允许你创建与特定时间相匹配的 UUID。这在测试或将项目插入已排序序列的特定位置时非常有用。

⏳实际上,Guid.CreateVersion7()会在内部使用NewGuid()。它包含了一个48位的时间戳,并设置了版本和变体位,使其符合UUIDv7标准。这会使它稍微比NewGuid()慢一些。这点需要注意,但是这样的性能影响通常可以忽略不计。

10. 其他特性

以下是一些其他有趣的更改,绝对不容错过,但可能不会被广泛采用,而是解决更为具体的问题场景。

觉得有帮助就点赞👏和反馈哦!非常宝贵!

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消