C# 13 中的新增功能实操

前言

今天大姚带领大家一起来看看 C# 13 中的新增几大功能,并了解其功能特性和实际应用场景。

前提准备

要体验 C# 13 新增的功能可以使用最新的 Visual Studio 2022 版本或 .NET 9 SDK 尝试这些功能。

Visual Studio 2022安装

.NET 9 SDK

params 集合

在 C# 13 中,params 的改进使其从可变数量的数组参数升级为可变数量的集合类型参数。这一改进通过支持高性能集合类型(如Span<T>、ReadOnlySpan<T>)和简化调用语法,显著提升了代码的灵活性和效率。

C# 13 中的 params 集合变得类型更灵活,满足更复杂的应用场景,并且性能得到了进一步的提升。

在 C# 13 之前:

params 仅支持一维数组(如params int[] listparams object[] list)。调用方法时需显式传递数组或数组元素类型的参数的逗号分隔列表。

在 C# 13 中:

params 修饰符并不局限于数组类型。 现在可以将 params 用于任何已识别的集合类型,包括 System.Span<T>、System.ReadOnlySpan<T>,以及那些实现 System.Collections.Generic.IEnumerable<T> 并具有 Add 方法的类型。 除了具体类型外,还可以使用接口 System.Collections.Generic.IEnumerable<T>、System.Collections.Generic.IReadOnlyCollection<T>、System.Collections.Generic.IReadOnlyList<T>、System.Collections.Generic.ICollection<T>和 System.Collections.Generic.IList<T>

csharp 复制代码
        public static void SpanDataPrintRun()
        {
            Span<int> originalSpan = [1, 2, 3, 4, 5];
            SpanDataPrint(originalSpan);
        }

        public static void SpanDataPrint<T>(params Span<T> spans)
        {
            for (int i = 0; i < spans.Length; i++)
            {
                Console.WriteLine(spans[i]);
            }
        }

新增Lock锁对象

.NET 9 包含一种新的用于互斥的 System.Threading.Lock 类型,比仅在任意 System.Object 实例上进行锁定更有效。该类型通过其 API 提供更好的线程同步,通过Lock.EnterScope()返回的ref struct自动管理锁的释放,减少死锁风险。

csharp 复制代码
        private object _oldLock = new object();
        private System.Threading.Lock _newLock = new System.Threading.Lock();

        public void LockTest()
        {
            lock (_oldLock)
            {
                Console.WriteLine("Old lock");
            }


            lock (_newLock)
            {
                // 传统 lock 语法(优化版)
            }

            using (_newLock.EnterScope())
            {
                //  作用域自动释放(推荐写法)
            }

            _newLock.Enter();
            try
            {
                // 显式 Enter/Exit 调用
            }
            finally { _newLock.Exit(); }

            if (_newLock.TryEnter())
            {
                try
                {
                    // 非阻塞尝试获取锁
                }
                finally { _newLock.Exit(); }
            }

        }

新的转义序列

新增 \e 转义字符作为 ESCAPE 字符 Unicode U+001B 的字符文本转义序列。以前,只能使用的是 \u001b\x1b。不建议使用\x1b,因为如果 1b 后面的下一个字符是有效的十六进制数字,则那些字符会成为转义序列的一部分。

arduino 复制代码
        public static void NewEscapeSequence()
        {
            Console.WriteLine("[31m红色文本[0m");

            // C# 13 之前
            Console.WriteLine("\u001b[31m红色文本\u001b[0m"); //输出红色文字

            // C# 13 中
            Console.WriteLine("\e[31m红色文本\e[0m");//功能相同,语法更简洁
        }

方法组自然类型改进

此功能对涉及方法组的重载解析进行了少量优化。方法组是一个方法,并且所有重载都具有相同的名称。 编译器以前的行为是为方法组构造完整的候选方法集。如果需要自然类型,则自然类型是根据整套候选方法确定的。

csharp 复制代码
// C# 13 中可以直接使用方法组并推断自然类型:
var a = Example.Method; // 成功推断为Action<int>(选择第一个匹配的委托类型)

// 通过上下文进一步优化推断:
List<Action<int>> actions = new() { Example.Method }; // 根据集合类型推断为Action<int>

隐式索引访问

在C# 13中允许在对象初始化表达式中使用^ 运算符(从末尾索引)直接为集合元素赋值。

csharp 复制代码
        public class Numbers
        {
            public int[] Datas { get; set; } = new int[8];
        }

        public static void ImplicitIndexAccess()
        {
            var countdown = new Numbers()
            {
                Datas =
                {
                   [1] = 0,
                   [2] = 1,
                    
                   // 从 C# 13 开始可以执行下面方式赋值
                   [^3] = 2,
                   [^4] = 3,
                   [^5] = 4
                }
            };
        }

异步和迭代器方法中的 ref 与 unsafe 支持

在 C# 13 之前,迭代器方法(使用 yield return 的方法)和 async 方法不能声明局部 ref 变量,也不能使用 unsafe 上下文。

在 C# 13 中,async 方法可以声明 ref 局部变量或 ref struct 类型的局部变量。 但不可跨 awaityield return 使用。

同样,C# 13 允许在迭代器方法中使用 unsafe 上下文。但是,所有 yield returnyield break 语句都必须在安全的上下文中。

应用场景:提升内存敏感操作(如高性能 Span 处理)的灵活性。

allows ref struct

在 C# 13 之前,ref struct 类型不能声明为泛型或方法的类型参数。现在,泛型类型声明可以添加反约束 allows ref struct。 此反约束声明为该类型参数提供的类型参数可以是 ref struct 类型。编译器会对该类型参数的所有实例执行 ref 安全规则。

  • 应用场景:适用于游戏引擎、实时数据处理等需要低延迟内存操作的领域。通过泛型约束,可编写同时支持 ref struct 和非 ref struct 的通用代码。

例如,可以像下面的代码一样声明一个泛型类型:

csharp 复制代码
public class C<T> where T : allows ref struct
{
    // 使用 T 作为 ref struct:
    public void M(scoped T p)
    {
        //参数 p 必须遵循 ref 安全规则 
    }
}

allows ref struct 反约束声明相应的类型参数可以是 ref struct 类型。 该类型参数的实例必须遵循以下规则:

  • 它不能被装箱。
  • 它参与引用安全规则。
  • 不能在不允许 ref struct 类型的地方使用实例,例如 static 字段。
  • 实例可以使用 scoped 修饰符进行标记。

partial类型中现在允许使用部分属性和索引器

现如今可以在 C# 13 中声明 partial属性partial索引器partial属性和索引器通常遵循与partial方法相同的规则:创建一个定义声明,一个实现声明。这两种声明的签名必须匹配。一个限制是,不能使用自动属性声明来实现部分属性。未声明正文的属性被视为声明声明。

注意:不允许对构造函数、终结器、重载运算符或事件声明使用 partial 关键字。在 C# 13 之前,不允许对属性或索引器使用partial。

vbnet 复制代码
public partial class MyClass
{
    public partial string Name { get; set; }
}

public partial class MyClass
{
    private string _name;
    public partial string Name
    {
        get => _name;
        set => _name = value;
    }
}

重载解析优先级

在 C# 13 中,编译器识别 OverloadResolutionPriorityAttribute,以便优先选择一个重载而不是另一个。库作者可以使用该属性确保新的、更好的重载比现有的重载更受青睐。

应用场景:适用于解决特定场景下的重载冲突和性能优化需求。通过合理设置优先级,开发者可以在保持代码兼容性的同时,优化编译器的选择逻辑。

csharp 复制代码
    public class Printer
    {
        [OverloadResolutionPriority(1)] //优先调用
        public static void PrintWay(params int[] numberList) { }

        public static void PrintWay(params ReadOnlySpan<int> numberList) { }
    }

参考文章

相关推荐
唐青枫3 小时前
C#.NET Configuration 全面解析:从多环境到强类型绑定实战
c#·.net
IT_陈寒3 小时前
Vite 3.0 性能优化实战:5个技巧让你的构建速度提升200% 🚀
前端·人工智能·后端
程序新视界4 小时前
MySQL的整体架构及功能详解
数据库·后端·mysql
绝无仅有4 小时前
猿辅导Java面试真实经历与深度总结(二)
后端·面试·github
绝无仅有4 小时前
猿辅导Java面试真实经历与深度总结(一)
后端·面试·github
Victor3565 小时前
Redis(76)Redis作为缓存的常见使用场景有哪些?
后端
Victor3565 小时前
Redis(77)Redis缓存的优点和缺点是什么?
后端
摇滚侠8 小时前
Spring Boot 3零基础教程,WEB 开发 静态资源默认配置 笔记27
spring boot·笔记·后端
天若有情67310 小时前
Java Swing 实战:从零打造经典黄金矿工游戏
java·后端·游戏·黄金矿工·swin
一只叫煤球的猫11 小时前
建了索引还是慢?索引失效原因有哪些?这10个坑你踩了几个
后端·mysql·性能优化