前言

微软即将在 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])来初始化数组元素。

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

当运行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(); // 切换上下文 // 继续使用 p
for (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技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

.NET 9 优化,抢先体验 C# 13 新特性的更多相关文章

  1. jdk 9 10 11 12 13 新特性

    jdk 9 新特性 1.集合加强 jdk9 为所有集合(List/Set/Map)都增加了 of 和 copyOf 方法,用来创建不可变集合,即一旦创建就无法再执行添加.删除.替换.排序等操作,否则将 ...

  2. 大数据开发-Flink-1.13新特性

    介绍 大概4月,Flink1.13就发布了,参加 了Flink1.13 的Meetup,收获还是挺多,从大的方面讲就是FlingSql的改进和优化,资源调度管理方面的优化,以及流批一体Flink在运行 ...

  3. Android 13 新特性及适配指南

    Android 13(API 33)于 2022年8月15日 正式发布(发布时间较往年早了一些),正式版Release源代码也于当日被推送到AOSP Android开源项目. 截止到笔者撰写这篇文章时 ...

  4. oracle11g中SQL优化(SQL TUNING)新特性之SQL Plan Management(SPM)

    1.   简介 Oracle Database11gR1引进了SQL PlanManagement(简称SPM),一套允许DBA捕获和保持任意SQL语句执行计划最优的新工具,这样,限制了刷新优化器统计 ...

  5. oracle11g中SQL优化(SQL TUNING)新特性之Adaptive Cursor Sharing (ACS)

    1.   ACS简介 Oracle Database 11g提供了Adaptive Cursor Sharing (ACS)功能,以克服以往不该共享的游标被共享的可能性.ACS使用两个新指标:sens ...

  6. Java 13 新特性

    一.core-libs/java.nio 添加 FileSystems.newFileSystem(Path, Map<String, ?>) 方法 java.nio.file.FileS ...

  7. Oracle12c中SQL性能优化(SQL TUNING)新特性之自动重优化(automatic reoptimization)

    Oracle12c中的自动重优化 Oracle12c中的自适应查询优化有一系列不同特点组成.像自适应计划(AdaptivePlans)功能可以在运行时修改执行计划,但并不允许计划中连接顺序的改变.自动 ...

  8. Oracle12c中SQL优化(SQL TUNING)新特性之SQL计划指令

    SQL计划指令是Oracle12c中自适应查询优化的功能之一.SQL计划指令就像“额外的提醒” ,用以提醒优化器你先前选择了的计划并不是最优的,典型的是因为错误的势评估.错误的势评估往往是由统计信息缺 ...

  9. Java 13新特性

    switch表达式 switch表达式是Java 12开始就提供的预览特性,到了Java 13仍然没有被转正.此特性可以把switch的执行结果组合到复合表达式中进行运算. import java.t ...

  10. JDK8~13新特性概览

    JDK8 1. 接口default 与 static 关键字 /** * jdk8中接口可以使用声明default和static修饰的方法 * static修饰的方法和普通的方法一样,可以被直接调用 ...

随机推荐

  1. 【HDC 2024】华为云开发者联盟驱动应用创新,赋能开发者成长

    本文分享自华为云社区<[HDC 2025]华为云开发者联盟驱动应用创新,赋能开发者成长>,作者:华为云社区精选. 6月21日到23日,华为开发者大会(HDC 2024)于东莞松山湖举行,这 ...

  2. git连接到https服务器时出现“gnutls_handshake() failed”

    git连接到https服务器时出现"错误: gnutls_handshake()失败" 问题描述 当我尝试使用git连接到任何HTTPS服务器时(例如git clone),它会出现 ...

  3. ZYNQ:PetaLinux工程更新HDF文件的脚本

    PetaLinux工程更新HDF文件的脚本 参考:PetaLinux工程更新HDF文件的脚本 工程师可能同时使用多个Vivado工程,以便测试不同的硬件配置.如果能够让一个PetaLinux工程支持多 ...

  4. VS Code Go开发环境配置

    1.安装Go 下载网址:https://go.dev/doc/install 根据自己的操作系统来进行安装,官网针对Windows.Linux.macOS都有对应教程.安装完成后打开终端,输入go v ...

  5. 【深度学习 有效炼丹】多GPU使用教程, DP与DDP对比, ray多线程并行处理等 [GPU利用率低的分析]

    ️ 前言 更新日志: 20220404:新增一个DDP 加载模型时显存分布不均问题,见目录遇到的问题及解决处 主要是上次server12 被自己一个train 直接线程全部拉满了(没错 ... ser ...

  6. css定位 position:sticky

    今天在做css定位的时候遇到一个问题,我想用fixed定位下来,但是发现这个时候定义的百分百宽度不随着父元素走了而是整个屏幕的百分百,这个就很尴尬了,也不能固定宽度吧,毕竟还要宽度自适应. 这个时候发 ...

  7. React中的Ref

    React中ref是一个对象,它有一个current属性,可以对这个属性进行操作,用于获取DOM元素和保存变化的值.什么是保存变化的值?就是在组件中,你想保存与组件渲染无关的值,就是JSX中用不到的或 ...

  8. java中的即时编译(JIT)简介

    Java发展这么多年一直长青,很大一部分得益于开发人员长期对其坚持不懈的优化:写得更少,跑得更快!JIT就是其中一项十分重要的优化. JIT全程Java Intime Compiler,即Java即时 ...

  9. SpirngBoot整合MybatisPlus 附源码

    项目搭建 目录结构 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns ...

  10. C#计算两个日期的天数

    private int DateDiff(DateTime dateStart, DateTime dateEnd) { DateTime start = Convert.ToDateTime(dat ...