July 23rd, 2024

C# 13:探索最新的预览功能 

Mia Wu
Partner Technical Advisor

本篇翻译于Kathleen Dollard 的C# 13: Explore the latest preview features – .NET Blog (microsoft.com) 

C# 13 已初具雏形其新特性侧重于灵活性、性能以及使您最喜欢的功能在日常中变得更容易使用。我们以公开的方式构建 C#,在今年的 Microsoft Build 大会上,我们会让您一睹 C# 13 的精彩之处。今天,我们想分享一下C# 13 当前的状态,以及您今天就可以尝试的新功能,并提供有关此版本及以后计划中的功能更新。让我们更详细地了解这些新功能。 

立即试用 C# 13 

在我们深入研究 C# 13 的每个新功能之前,您可能想知道如何使用它。 

您可以在最新的 .NET 9 预览版(撰写本文时为预览版 6)和 Visual Studio 2022-17.11 的最新预览版中找到 C# 13 的最新预览版。若要访问预览功能,请在项目文件中将您的语言版本设置为preview 

<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
      <!--other settings-->
      <LangVersion>preview</LangVersion>
      <!--other settings-->
   </PropertyGroup>
</Project>

params集合 

C# 13扩展了 params关键字,使其适用于可以通过集合表达式构造的任何类型。无论是编写方法还是调用方法,都增加了灵活性 

params 关键字出现在参数之前时,对该方法的调用可以提供零个或多个值的逗号分隔列表。以下内容适用于所有版本的 C#: 

public void WriteNames(params string[] names)
   => Console.WriteLine(String.Join(", ", names));

WriteNames("Mads", "Dustin", "Kathleen");
WriteNames(new string[] {"Mads", "Dustin", "Kathleen"});
// Both of these Would output: Mads, Dustin, Kathleen

请注意,您可以使用逗号分隔的值列表或基础类型的对象来调用该方法。 

C# 13 开始,params 参数可以是集合表达式支持的任何类型。例如: 

public void WriteNames(params List<string> names)
   => Console.WriteLine(String.Join(", ", names));

每当调用具有 IEnumerable<T> 参数的方法时,都可以传递 LINQ 表达式的结果。如果 IEnumerable<T> 参数具有 params 修饰符,还可以传递逗号分隔列表。当有常量,可以使用逗号分隔列表;当您需要LINQ表达式时,您可以使用它 

public void WriteNames(params IEnumerable<string> names)
   => Console.WriteLine(String.Join(", ", names));

var persons = new List<Person>
{
   new Person("Mads", "Torgersen"),
   new Person("Dustin", "Campbell"),
   new Person("Kathleen", "Dollard")
};

// All of the following output: Mads, Dustin, Kathleen
WriteNames("Mads", "Dustin", "Kathleen");
WriteNames(persons.Select(person => person.FirstName));
WriteNames(from p in persons select p.FirstName);

重载解析 

编写方法时,可以提供多个params重载。例如,添加 IEnumerable<T> 重载可支持 LINQ,添加 ReadOnlySpan<T> Span<T> 重载可减少分配,从而可以提高性能 

public void WriteNames(params string[] names)
   => Console.WriteLine(String.Join(", ", names));

public void WriteNames(params ReadOnlySpan<string> names)
   => Console.WriteLine(String.Join(", ", names));

public void WriteNames(params IEnumerable<string> names)
   => Console.WriteLine(String.Join(", ", names));

当传递指定类型之一时,将使用该重载。当传递逗号分隔值或不传递任何值时,将选择最佳重载。使用上述重载: 

// IEnumerable overload is used
WriteNames(persons.Select(person => person.FirstName)); 

// array overload is used
WriteNames(new string[] {"Mads", "Dustin", "Kathleen"}); 

// most efficient overload is used: currently ReadOnlySpan
WriteNames("Mads", "Dustin", "Kathleen");                

多重重载可以增加便利性并提高性能。库作者应该为所有重载提供相同的语义,这样调用者就不需要关心使用了哪个重载 

lock对象 

.NET 9 包含一种新的用于互斥的 System.Threading.Lock 类型,比仅在任意 System.Object 实例上进行锁定更有效。System.Threading.Lock 类型提案详细介绍了该类型及其创建原因。随着时间的推移,该类型有望成为 C# 代码中大多数锁定的主要机制 

C# 13 使该类型的使用变得简单。当编译器识别出 lock 语句的目标是 System.Threading.Lock 对象时,C# 就会生成对 System.Threading.Lock API 的调用,并在 Lock 实例可能被错误地视为普通object的情况下提供警告 

此更新意味着 lock 语句的熟悉语法利用了运行时中的新功能。熟悉的代码只需进行少量更改即可变得更好。只需将项目的 TargetFramework 更改为 .NET 9,并将lock的类型从object更改为 System.Threading.Lock 

public class MyClass 
{
    private object myLock = new object();

    public void MyMethod() 
    {
        lock (myLock)
        {
           // Your code
        }          
    }
}

public class MyClass 
{
    // The following line is the only change
    private System.Threading.Lock myLock = new System.Threading.Lock();

    public void MyMethod() 
    {
        lock (myLock)
        {
            // Your code
        }     
    }
}

从初始化器末尾开始的索引 

索引运算符 ^ 允许您在可计数集合中相对于集合末尾指定一个位置。这现在适用于初始化器 

class Program
{ 
    static void Main()
    {
        var x = new Numbers
        {
            Values = 
            {
                [1] = 111,
                [^1] = 999  // Works starting in C# 13
            }
            // x.Values[1] is 111
            // x.Values[9] is 999, since it is the last element
        };
    }
}

class Numbers
{
    public int[] Values { get; set; } = new int[10];
} 

转义序列 \e 

C# 13 为众所熟知的 ESCAPE ESC 字符引入了新的转义序列。以前您必须将其输入为 \u001b 的变体。当使用 VT100/ANSI 转义码与 System.Console 的终端交互时,此新序列特别方便。例如 

// Prior to C# 13
Console.WriteLine("\u001b[1mThis is a bold text\u001b[0m");

// With C# 13
Console.WriteLine("\e[1mThis is a bold text\e[0m");

Console.WriteLine(“\e[1mThis is a bold text\e[0m”); 

这使得创建精美的终端输出变得更容易并且更不容易出错 

Partial属性 

C# 13 添加了partial属性。与partial方法一样,它们的主要目的是支持源生成器。partial方法已在许多版本中可用,并在 C# 9 中进行了额外的改进。partial属性与它们的partial方法非常相似 

例如,从 .NET 7(C# 12)开始,正则表达式源生成器为方法创建高效的代码 

[GeneratedRegex("abc|def")]
private static partial Regex AbcOrDefMethod();

if (AbcOrDefMethod().IsMatch(text))
{
   // Take action with matching text
}

.NET 9(C# 13)中,Regex 源生成器已更新,如果您更喜欢使用属性,您也可以使用 

[GeneratedRegex("abc|def")]
private static partial Regex AbcOrDefProperty { get; };

if (AbcOrDefProperty.IsMatch(text))
{
   // Take action with matching text
}

Partial属性将使源生成器设计人员更容易创建自然感觉的 API。 

方法组自然类型改进 

表达式的自然类型是由编译器确定的类型,例如当类型分配给 var Delegate 时。当它是一个简单类型时,这很简单。在 C# 10 中,我们添加了对方法组的支持。当您将不带括号的方法名称作为委托包含时,将使用方法组 

Todo GetTodo() => new(Id: 0, Name: "Name");
var f = GetTodo; // the type of f is Func<ToDo>

C# 13 完善了确定自然类型的规则,以便根据范围考虑候选者并删除没有成功机会的候选者。更新这些规则意味着在使用方法组时编译器错误会更少 

allows ref struct 

C# 13 添加了一种指定泛型类型参数功能的新方法。默认情况下,类型参数不能为ref struct但是C# 13 允许您指定类型参数可以是 ref struct,并应用相应的规则。虽然其他泛型约束限制了可用作类型参数的类型集,但此新规范扩展了允许的类型。我们认为这是一种反约束,因为它删除了限制,而不是添加了限制。where 子句中的语法allows ref struct,其中allows表示用法上的扩展 

T Identity<T>(T p)
    where T : allows ref struct
    => p;

// Okay
Span<int> local = Identity(new Span<int>(new int[10]));

使用allows ref struct 指定的类型参数具有 ref struct 类型的所有行为和限制 

async方法和迭代器中的 ref unsafe 

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

C# 13 中,async方法可以声明 ref 局部变量,或 ref struct 类型的局部变量。这些变量不能跨 await 边界或 yield return 边界保存 

同样,C# 13 允许迭代器方法中使用unsafe上下文。但是,所有 yield return await 语句都必须在safe上下文中。这些放宽的限制让您可以在更多地方使用 ref 局部变量和 ref struct类型。 

扩展类型的更新 

我们对 Mads Dustin Build 上展示的扩展类型功能感到非常兴奋。我们还在Build上的博客文章 .NET 公告中介绍了扩展类型。当时,我们的目标是在C# 13中实现该功能的关键部分,但设计和实现将需要更多的时间。请在早期的 C# 14(NET 10)预览版中查找扩展类型 

总结 

您可以在 C# 13 的新增功能中了解更多相关内容。我们仍在为C# 13 开发新功能,您可以在 Roslyn 功能状态页面查看我们正在做的事情。此外,请务必关注 .NET 9 预览版发行说明,您现在可以在其中找到每个版本的 C# 发行说明。 

请下​​载带有 .NET 9 Visual Studio 2022-17.11 的最新预览版,体验这些新功能,并告诉我们您的想法 

如果您有任何技术问题,欢迎来Microsoft Q&A 提问。

Author

Mia Wu
Partner Technical Advisor

0 comments