.NET 9 优化,抢先体验 C# 13 新特性

news/2024/11/15 13:39:09/文章来源:https://www.cnblogs.com/1312mn/p/18367817

前言

微软即将在 2024年11月12日发布 .NET 9 的最终版本,而08月09日发布的.NET 9 Preview 7 是最终发布前的最后一个预览版。这个版本将与.NET Conf 2024一同亮相,并已与 Visual Studio 2022 17.12 预览版1一同发布,可以直接通过Visual Studio安装。同时Visual Studio Code 和 C# Dev Kit 扩展也支持.NET 9。

C# 13 作为 .NET 9 的一部分,将带来一系列新特性,提升开发灵活性和性能,让编程体验更加流畅。尽管C# 13 尚未正式发布,但我们可以在 .NET 9 Preview 7 中尝试这些新特性,需要下载最新的 Visual Studio 2022 17.11 预览版。

注意:目前 C# 13 尚未正式发布,因此功能细节可能会有所调整。

新特性

1、params 集合增强,以提高灵活性

在 C# 13 中,params关键字的使用已经扩展到不仅仅是数组,还可以应用于任何可识别的集合类型,包括System.Span<T>、System.ReadOnlySpan<T>和实现了System.Collections.Generic.IEnumerable<T>的类型。

2、锁对象

.NET 9 运行时引入了System.Threading.Lock类型,提供了改进的线程同步机制。Lock类型通过其 API 支持更高效的线程同步操作,例如Lock.EnterScope()方法可以进入一个独占作用域

3、索引器改进

索引器的使用变得更加直观和灵活,能够更高效地操作集合。

4、转义序列\e

使用 \e 的好处是它可以避免与十六进制转义序列混淆。

5、部分属性

部分属性的引入使得属性的定义和实现可以分布在不同的文件中,提高了代码的组织性和可维护性。

6、方法组自然类型改进

方法组的自然类型得到了改进,使得调用变得更简单,减少了不必要的转换。

7、ref 和 unsafe 在 async 方法和迭代器中的使用

现在 async 方法和迭代器可以使用ref变量和不安全代码,可以在更多情况下使用这些特性,尽管仍然有一些限制。

8、关于扩展类型(Extension Types)的更新

C# 13 中一个非常重大的特性,它允许向现有类添加新的方法、属性、甚至静态成员,而无需修改原始类代码。

9、LINQ 新方法

新增了CountBy和AggregateBy方法,允许按键聚合状态而无需通过GroupBy分配中间分组,这为数据聚合提供了更灵活的方式

10、Foreach 支持 Index

引入了Index<TSource>(IEnumerable<TSource>),使得在 foreach 循环中可以快速提取可枚举项的索引

11、序列化改进

System.Text.Json在 .NET 9 中进行了改进,提供了新的选项用于 JSON 序列化,并引入了 JsonSerializerOptions.Web 单例,简化了使用 Web 默认值进行序列化的过程。

12、性能改进

.NET 9 在异常处理、环路性能、动态 PGO(按配置文件优化)、RyuJIT 编译器以及 Arm64 指令集支持方面进行了优化,显著提升了应用程序的性能。

Params 集合

params关键字允许方法接受一个参数列表,这个列表可以是任何实现了IEnumerable<T>接口的集合类型。

意味着可以使用方法参数来传递数组、列表、元组等集合,而不必显式地创建集合实例。

以下是一个使用 params关键字的简单示例:

using System;
using System.Collections.Generic;
using System.Linq;public class Program
{// 这个方法可以接受任意数量的字符串参数public static void PrintNames(params string[] names){Console.WriteLine("Names provided:");foreach (var name in names){Console.WriteLine(name);}}public static void Main(){// 直接传递字符串参数PrintNames("Alice", "Bob", "Charlie");// 使用数组string[] namesArray = new string[] { "Dave", "Eve", "Frank" };PrintNames(namesArray);// 使用列表List<string> namesList = new List<string> { "Grace", "Heidi", "Ivan" };PrintNames(namesList);// 使用 LINQ 表达式var query = from person in new List<Person> {new Person("Judy", "Walker"),new Person("Kevin", "Smith")}select person.FirstName;PrintNames(query);// 使用从集合中选择的属性var persons = new List<Person>{new Person("Leonard", "Nimoy"),new Person("Morgan", "Freeman")};PrintNames(from p in persons select p.FirstName);}
}public class Person
{public string FirstName { get; }public string LastName { get; }public Person(string firstName, string lastName){FirstName = firstName;LastName = lastName;}
}

在这个示例中,PrintNames方法使用params关键字来接受任意数量的字符串参数。可以使用多种方式调用这个方法:

  • 直接传递字符串字面量。

  • 传递一个字符串数组。

  • 传递一个字符串列表。

  • 使用 LINQ 查询来传递查询结果。

  • 使用 LINQ 从Person对象的集合中选择FirstName属性。

这个示例展示了params集合的灵活性,允许以多种不同的集合类型传递参数,而方法内部的实现保持不变。

锁对象

众所周知,lock 是一种功能,通过监视器用于线程同步。

object lockObject = new object();
lock (lockObject)
{// 关键区
}

但是,这个功能的开销其实很大,会影响性能。为了解决这个问题,C# 13 实现了锁对象。要使用此功能,只需用 System.Threading.Lock 替换被锁定的对象即可:

using System.Threading;Lock lockObject = new Lock();
lock (lockObject)
{// 关键区
}

这样就可以轻松提高性能了。

索引器改进

对索引器的改进,其中包括在对象初始化器中使用”尾部索引"(也称为“从末尾开始的索引”)的能力。

这种索引方式允许从集合的末尾开始计数,使用 ^ 符号来指定元素的位置。

以下是 C# 13 中索引器改进的示例:

using System;public class Demo
{public static void Main(){// 定义一个可索引的类型var data = new IndexedData{// 使用传统的索引器初始化Items = { [2] = "Second", [3] = "Third" },// 使用尾部索引初始化[^1] = "First", // 从末尾开始的第一个元素[^2] = "Fourth" // 从末尾开始的第二个元素
        };// 打印初始化后的数据for (int i = 0; i < data.Items.Length; i++){Console.WriteLine($"Index {i}: {data.Items[i]}");}}
}public class IndexedData
{public string[] Items { get; set; } = new string[5];
}

在这个示例中,IndexedData 类有一个名为 Items 的字符串数组属性。

在初始化 data 对象时,我们使用了两种索引方式:

  • 传统的索引器,通过指定索引位置(例如 [2] 和 [3])来初始化数组元素。

  • 尾部索引器,使用 ^ 符号后跟数字来指定从数组末尾开始的位置(例如 12)。

当运行Main方法时,它将打印出数组中每个元素的索引和值,包括使用尾部索引初始化的元素。

输出结果将是:

Index 0: 
Index 1: 
Index 2: Second
Index 3: Third
Index 4: First

请注意,尾部索引 1 被分配给了数组的最后一个位置(索引4),而 2被分配给了倒数第二个位置(索引3),这是因为它们是从末尾开始计数的。这种特性在初始化数组或集合时特别有用,尤其是当你需要在已知末尾元素的情况下进行初始化时。 转义序列 \e在 Unicode 字符串中,可以使用\e 来代表 ESCAPE 字符,它等同于传统的\u001b 或\x1b。

  • \u001b 是一个 Unicode 转义序列,其中 \u 后跟的四位十六进制数代表一个 Unicode 点。

  • \x1b 是一个十六进制转义序列,\x 后面跟的两位十六进制数代表一个 ASCII 字符。

  • \e 直接表示 ESCAPE 字符,它避免了可能的混淆。

推荐使用 \e 是因为它提供了一种清晰无歧义的方式来表示 ESCAPE 字符。例如,\x1b 后如果紧跟数字可能会造成混淆,如 \x1b3 可能被误解为单一的转义序列。使用 \e 就可以清楚地表达 ESCAPE 字符,避免了这种混淆。

部分属性

在 C# 13 之前,属性不支持使用partial修饰符,这意味着属性的声明和实现必须在同一个位置完成。这在自动生成代码或分离关注点时可能会带来限制。

C# 13 改进了这一点,允许属性跨越多个部分进行声明和实现。特性特别适用于与源代码生成器等工具结合使用的场景,可以更灵活地生成和管理属性代码。

以下是 C# 13 中属性支持partial的示例:

public class DemoModel
{//声明部分属性public partial int MyProperty { get; set; }
}public class DemoModel
{// 部分属性的实现public partial int MyProperty{get { return GetValue(); }set { SetValue(value);   }}
}

这种方式可以专注于属性的业务逻辑部分,而将具体的实现细节留给自动化工具处理,从而提高开发效率并减少重复性编码工作。

方法组自然类型

方法组的自然类型改进允许编译器更精确地确定方法的自然类型,特别是在重载解析时。这意味着编译器可以更有效地识别应该使用哪个重载版本,尤其是在涉及委托和方法组的情况下。

以下是一个示例,展示了 C# 13 中方法组自然类型的改进:

using System;public class Program
{public static void Main(){// 声明一个委托类型,它指向一个接受 Action 作为参数的方法Action<string> action = PrintMessage;// 调用 PrintMessage 方法,使用方法组作为参数action("Hello, World!");}// 这是原始的重载版本public static void PrintMessage(string message){Console.WriteLine($"Original: {message}");}// C# 13 允许更精确的自然类型推断public static void PrintMessage(Action<string> messagePrinter, string message){messagePrinter(message);Console.WriteLine("Improved natural type inference in C# 13.");}
}

在这个示例中,PrintMessage方法有两个重载。第一个重载接受一个string参数,而第二个重载接受一个Action<string>和一个string参数。

在 C# 13 之前,如果尝试使用方法组调用action委托,编译器可能会在重载解析时产生模糊性,因为它需要确定使用哪个重载。

C# 13 中的方法组自然类型改进允许编译器更准确地推断出应该使用第一个 PrintMessage 重载,因为它更匹配传递的参数类型(一个字符串)。第二个重载虽然也能接受字符串,但它期望的是一个Action<string>类型的参数,这在方法组调用中是不匹配的。

请注意,这个示例仅用于说明 C# 13 中方法组自然类型改进的概念。在实际代码中,可能需要根据具体情况调整方法签名和调用方式。

ref 和 unsafe 在 async 方法和迭代器中的使用

在 C# 13 之前,ref 和 unsafe 关键字在异步方法(使用 async和 await 修饰的方法)和迭代器中有一些限制。

然而,C# 13 放宽了这些限制,可以在这些上下文中使用 ref 和 unsafe。

以下是一些示例,展示在 C# 13 中如何在异步方法和迭代器中使用 ref 和 unsafe:

1、在异步方法中使用ref

async Task RefInAsyncMethod()
{int value = 0;await Task.Yield();ref int local = ref ModifyValue(ref value);local++; // 修改原始变量的值Console.WriteLine(value); // 输出修改后的值
}ref int ModifyValue(ref int x)
{return ref x;
}

在这个示例中,ModifyValue方法返回对传入引用的引用。在异步方法RefInAsyncMethod中,我们使用await Task.Yield();来切换到另一个上下文,然后通过ref返回的引用来修改原始变量的值。

2、在迭代器中使用ref

IEnumerable<int> GetNumbers()
{int number = 0;yield return number; // 返回第一个值number++;            // 修改状态yield return number; // 返回修改后的值
}// 使用迭代器
foreach (int num in GetNumbers())
{Console.WriteLine(num);
}

在这个示例中,迭代器GetNumbers使用yield return来返回序列中的值。

在两次yield调用之间,迭代器的状态(number 变量)被保持,允许在第二次迭代时返回修改后的值。

3、在异步方法中使用unsafe

async Task UnsafeInAsyncMethod()
{unsafe{int* p = stackalloc int[10];for (int i = 0; i < 10; i++){p[i] = i;}await Task.Yield(); // 切换上下文// 继续使用 pfor (int i = 0; i < 10; i++){Console.WriteLine(p[i]);}}
}

在这个示例中,unsafe上下文被用在异步方法UnsafeInAsyncMethod中。我们使用stackalloc在栈上分配内存,并在await之前和之后访问这个内存。

这展示了即使在异步方法中,也可以执行不安全操作。

4、注意事项

  • 在异步方法中使用 ref和 unsafe需要谨慎,因为await会导致方法的执行上下文被挂起和恢复,这可能会影响对 ref 局部变量和 unsafe 代码的预期行为。

  • 确保在使用 ref 和 unsafe代码时,遵守 C# 的安全和并发规则。

C# 13 的这些改进提供了更大的灵活性,可以在异步编程和迭代器中使用ref和unsafe代码,但同时也需要更多的注意来确保代码的正确性和安全性。

总结

C# 13 带来的新特性和改进,如扩展类型的灵活性、params 关键字的增强、在异步方法中使用ref 和unsafe的能力,以及对序列化性能的优化等,都极大地提升了我们开发效率,解决了很多实际开发中遇到的问题。

对 .NET 9 和 C# 13 的正式发布充满期待,相信将为社区带来更加强大和便捷的工具,进一步推动技术的更新和发展。下载最新的 Visual Studio 2022-17.11 预览版,可以亲自体验这些新特性。

下载地址

下载.NET 9.0

Visual Studio 2022 预览版

参考链接

《C# 13: Explore the latest preview features》

《提高 C# 的生产力:C# 13 更新完全指南》

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。也可以加入微信公众号[DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/784168.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

原生JS实现虚拟列表

什么是虚拟列表 如果我们要将一个数组渲染为列表添加到页面中,我们可以很容易实现,无非就是循环遍历这个数组,然后依次创建 DOM 元素插入即可,但是如果数据量很庞大,比如有一万条数据,我们就要把一万个 DOM 结点插入到页面中,这显然会导致页面的卡顿。为了针对这个场景进…

高德/百度地图: 坐标拾取系统:用经纬度搜索

一,高德地图:坐标拾取器 https://developer.amap.com/tools/picker 如图: 二,百度地图:拾取坐标系统 https://api.map.baidu.com/lbsapi/getpoint/index.html

面试必备之TCP知识

概述 关于TCP的杂乱知识点,不成体系,毕竟TCP真的太复杂。 TCP,Transmission Control Protocol;IP,Internet Protocol,两者共同组成TCP/IP协议族,包含一系列构成互联网基础的网络协议。 OSI七层网络模型图片来自于OSI七层网络模型 OSI七层由于太过严格,所以并没有应用在…

分布式系列之限流组件

概述 在高并发场景下,请求量瞬间到达,后端服务器即使有缓存、集群主备、分库分表、容错降级等措施,也有可能扛不住这请求量,因此可考虑引入限流组件。限流的目的:防止恶意请求流量或流量超出系统承载。 应用场景:网关层校验流量,拦截非法请求,或直接抛弃部分流量(后来…

SQL跟踪

SQL跟踪 如何sql跟踪气死很简单。 1.先打开菜单栏【工具】–【SQL Server Profiler】 2、弹出如下登录界面,选择对应服务器名称,进行身份验证 3.连接成功后,弹出如下界面,选【事件选择】页签,勾选第4、5个选项,并勾选【显示所有列】 4.然后点【1.列筛选器】弹出【编辑筛…

哇!0.8秒启动!Linux快速启动方案分享,全志T113-i国产平台!

本文主要介绍基于创龙科技TLT113-EVM评估板(基于全志T113-i)的系统快速启动方案,适用开发环境如下。Windows开发环境:Windows 7 64bit、Windows 10 64bit 虚拟机:VMware15.5.5 Linux开发环境:Ubuntu18.04.4 64bit U-Boot:U-Boot-2018.07 Kernel:Linux-5.4.61、Linux-RT…

BVS:多强联手,李飞飞也参与的超强仿真数据生成工具,再掀数据狂潮 | CVPR 2024

BEHAVIOR Vision Suite(BVS)是一个新型工具包,旨在系统评估和全面理解计算机视觉模型。研究人员能够在场景、对象和相机级别控制各种参数,有助于创建高度定制的数据集。 来源:晓飞的算法工程笔记 公众号论文: BEHAVIOR Vision Suite: Customizable Dataset Generation via…

9k star 监控系统,100% 国产,推荐了解

前言 监控系统的重要性不言而喻,国内用的最多的应该是 Zabbix 和 Prometheus,其优缺点:Zabbix 是资产管理式,监控数据存在数据库中,擅长设备监控,不擅长微服务和云原生环境的监控;推出时间较早,社区活跃度较高 Prometheus 是云原生环境的监控利器,支持多维度的指标数据…

DMS:直接可微的网络搜索方法,最快仅需单卡10分钟 | ICML 2024

Differentiable Model Scaling(DMS)以直接、完全可微的方式对宽度和深度进行建模,是一种高效且多功能的模型缩放方法。与先前的NAS方法相比具有三个优点:1)DMS在搜索方面效率高,易于使用。2)DMS实现了高性能,可与SOTA NAS方法相媲美。3)DMS是通用的,与各种任务和架构…

ArchLinux配置OpenCV C++环境

本文将简单介绍在 ArchLinux 中安装 OpenCV C++ 库并运行一个简单的 OpenCV 程序的过程。 参考:https://github.com/donaldssh/Install-OpenCV 我的环境最新的 ArchLinux KDE Plasma 6 桌面环境 OpenCV 4.10.0 clang 18.1.8 gcc 14.2.1安装 安装以下包: sudo pacman -S hdf5 …

聊聊如何利用ingress-nginx实现应用层容灾

前言 容灾是一种主动的风险管理策略,旨在通过构建和维护异地的冗余系统,确保在面临灾难性事件时,关键业务能够持续运作,数据能够得到保护,从而最大限度地减少对组织运营的影响和潜在经济损失。因此容灾的重要性不言而喻,今天的话题主要是聊下如何利用ingress-nginx实现应…

一个超全的go工具库Lancet

文档官网 https://www.golancet.cn 安装 使用 go1.18 及以上版本的用户,建议安装 v2.x.x。 因为 v2.x.x 应用 go1.18 的泛型重写了大部分函数。 go get github.com/duke-git/lancet/v2使用 go1.18 以下版本的用户,必须安装 v1.x.x。目前最新的 v1 版本是 v1.4.1。 go get git…