C# 14 中的新增功能

C# 14 包含以下新功能。 可以使用最新的 Visual Studio 2022 版本或 .NET 10 SDK 试用这些功能:

.NET 10 支持 C# 14。 有关详细信息,请参阅 C# 语言版本控制

可以从 .NET 下载页下载最新的 .NET 10 SDK。 还可以下载 Visual Studio 2022,其中包括 .NET 10 SDK。

新功能一旦在公开预览版中发布,就会被添加到“C# 新特性”页中。 roslyn 功能状态页工作集部分跟踪即将推出的功能何时合并到主分支中。 本文最后更新于 .NET 10 预览 1。

可以在我们的文章《重要更改》中查找 C# 14 中引入的任何 重大更改

注释

我们对这些功能的反馈感兴趣。 如果发现上述任何新功能的问题,请在 dotnet/roslyn 存储库中创建一个新问题

扩展成员

C# 14 添加了用于定义 扩展成员的新语法。 借助新语法,除了扩展方法之外,还可以声明 扩展属性 。 还可以声明扩展类型的扩展成员,而不是类型的实例。 换句话说,这些新的扩展成员可以作为扩展类型的静态成员出现。 下面的代码示例演示了可以声明的不同类型的扩展成员的示例:

public static class Enumerable
{
    // Extension block
    extension<TSource>(IEnumerable<TSource> source) // extension members for IEnumerable<TSource>
    {
        // Extension property:
        public bool IsEmpty => !source.Any();
        // Extension indexer:
        public TSource this[int index] => source.Skip(index).First();

        // Extension method:
        public IEnumerable<TSource> Where(Func<TSource, bool> predicate) { ... }
    }

    // extension block, with a receiver type only
    extension<TSource>(IEnumerable<TSource>) // static extension members for IEnumerable<Source>
    {
        // static extension method:
        public static IEnumerable<TSource> Combine(IEnumerable<TSource> first, IEnumerable<TSource> second) { ... }

        // static extension property:
        public static IEnumerable<TSource> Identity => Enumerable.Empty<TSource>();
    }
}

第一个扩展块中的成员被调用时,就好像它们是IEnumerable<TSource>的实例成员,例如sequence.IsEmpty。 第二个扩展块中的成员被调用时,如同它们是IEnumerable<TSource>的静态成员,例如IEnumerable<int>.Identity

可以通过阅读有关编程指南中的扩展成员的文章、有关关键字的语言参考文章extension以及新扩展成员功能的功能规范来了解更多详细信息。

field 关键字

使用令牌 field 可以编写属性访问器体,而无需声明后备字段。 令牌 field 将替换为编译器合成支持字段。

例如,以前,如果要确保 string 属性无法设置为 null,则必须声明一个后备字段并实现这两个访问器:

private string _msg;
public string Message
{
    get => _msg;
    set => _msg = value ?? throw new ArgumentNullException(nameof(value));
}

现在可以简化代码,以便:

public string Message
{
    get;
    set => field = value ?? throw new ArgumentNullException(nameof(value));
}

可以为字段支持的属性的一个或两个访问器声明一个主体。

如果类型中还包含一个名为 field 的符号,则在读取代码时可能会出现重大更改或混淆。 可以使用 @fieldthis.field 消除关键字和标识符之间的 field 歧义,也可以重命名当前 field 符号以提供更好的区别。

如果尝试此功能并收到反馈,请对存储库中的csharplang发表评论。

field 上下文关键字在 C# 13 中作为预览特性提供。

隐式跨度转换

C# 14 在语言中引入了对 System.Span<T>System.ReadOnlySpan<T> 的一流支持。 此支持涉及新的隐式转换,允许对这些类型进行更自然的编程。

在 C# 和运行时中,Span<T>ReadOnlySpan<T> 被用于多种关键方式。 他们的引入可提高性能,而不会造成安全风险。 C# 14 识别其相互关系,并支持在 ReadOnlySpan<T>Span<T>T[] 之间进行一些转换。 跨度类型可以作为扩展方法的接收器、与其他转换组合,或者在泛型类型推理场景中提供帮助。

可以在语言参考部分的 内置类型 的文章中找到隐式跨度转换的列表。 可以通过阅读 First 类跨度类型的功能规范来了解更多详细信息。

未绑定泛型类型和 nameof

从 C# 14 开始,nameof 的参数可以是未绑定的泛型类型。 例如,nameof(List<>) 计算为 List。 在早期版本的 C# 中,只能使用关闭的泛型类型(例如 List<int>)返回 List 名称。

带修饰符的简单 lambda 参数

可以在不指定参数类型的情况下,向 lambda 表达式参数添加参数修饰符,例如scopedrefinoutref readonly

delegate bool TryParse<T>(string text, out T result);
// ...
TryParse<int> parse1 = (text, out result) => Int32.TryParse(text, out result);

以前,仅当参数声明包含参数的类型时,才允许添加任何修饰符。 上述声明需要所有参数的类型:

TryParse<int> parse2 = (string text, out int result) => Int32.TryParse(text, out result);

修饰符 params 仍然需要显式输入的参数列表。

可以在有关 C# 语言参考中 lambda 表达式 的文章中详细了解这些更改。

更多部分成员

现在可以将 实例构造函数事件 声明为 部分成员

部分构造函数和分部事件必须包含一个 定义声明 和一个 实现声明

只有部分构造函数的实现声明可以包含构造函数初始值设定项: this()base()。 只有一个分部类型声明可以包含主构造函数语法。

部分事件的实现声明必须包含 addremove 访问器。 定义声明申明了一个类似字段的事件。

Null 条件分配

null 条件成员访问运算符 ?.?[] 现在可以被放在赋值或复合赋值表达式的左边。

在 C# 14 之前,在分配给属性之前,需要对变量进行 null 检查:

if (customer is not null)
{
    customer.Order = GetCurrentOrder();
}

可以使用运算符简化上述代码 ?.

customer?.Order = GetCurrentOrder();

运算符 = 的右侧仅在左侧不为 null 时才会被计算。 如果 customer 为 null,则代码不调用 GetCurrentOrder

除了赋值之外,还可以将 null 条件成员访问运算符与复合赋值运算符(+=-=和其他)一起使用。 但是,不允许递增 ++ 和递减 --

可以在语言参考文章中详细了解 条件成员访问null 条件分配的功能规范。

另请参阅