C# 15 中的新增功能

C# 15 包含以下新功能。 使用最新的 Visual Studio 2026 内部预览版或 .NET 11 预览版 SDK 试用这些功能:

C# 15 是最新的 C# 预览版。 .NET 11 预览版支持 C# 15。 有关详细信息,请参阅 C# 语言版本控制

可以从 .NET 下载页下载最新的 .NET 11 预览版 SDK。 还可以下载 Visual Studio 2026 内部预览版,其中包括 .NET 11 预览版 SDK。

“C# 中的新增功能”页面在公共预览版中可用时会添加新功能。 roslyn 功能状态页工作集部分跟踪即将推出的功能何时合并到主分支中。

您可以在我们关于重大变更的文章中找到 C# 15 中引入的任何重大变更

注释

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

集合表达式参数

可以用 with(...) 元素作为集合表达式的第一个元素,将参数传递给底层集合的构造函数或工厂方法。 借助此功能,可以直接在集合表达式语法中指定容量、比较器或其他构造函数参数。

以下示例演示如何将容量参数传递给List<T>构造函数,以及传递比较器给HashSet<T>

string[] values = ["one", "two", "three"];

// Pass capacity argument to List<T> constructor
List<string> names = [with(capacity: values.Length * 2), .. values];

// Pass comparer argument to HashSet<T> constructor
HashSet<string> set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"];
// set contains only one element because all strings are equal with OrdinalIgnoreCase

若要了解有关集合表达式参数的详细信息,请参阅有关集合表达式或特征规范的语言参考文章。 有关在集合初始值设定项中使用集合表达式参数的信息,请参阅 对象和集合初始值设定项

联合类型

C# 15 引入了 联合类型,这些类型表示可以是多种 事例类型之一的值。 使用 union 关键字声明联合:

public record class Cat(string Name);
public record class Dog(string Name);
public record class Bird(string Name);

public union Pet(Cat, Dog, Bird);

联合提供了从每种用例类型的隐式转换,并且编译器确保 switch 表达式在所有案例类型上是穷尽的:

Pet pet = new Dog("Rex");

string name = pet switch
{
    Dog d => d.Name,
    Cat c => c.Name,
    Bird b => b.Name,
};

运行时包括从 .NET 11 预览版 5 开始的 UnionAttributeIUnion 类型。 建议规范中的某些功能尚未实现。 这些功能将在未来预览版中推出。

有关详细信息,请参阅语言参考或功能规范中的联合类型

封闭层次结构

从 C# 15 开始,可以将修饰符应用于 closed 类来声明 封闭层次结构。 封闭类只能在其声明程序集内被派生,这会在编译时固定直接后代的集合:

public closed record class GateState;
public record class Closed : GateState;
public record class Open(float Percent) : GateState;

由于编译器知道每个直接后代,因此 switch 处理每个子代的表达式是详尽的,不需要默认 arm:

string Describe(GateState state) => state switch
{
    Closed => "closed",
    Open(var percent) => $"{percent}% open",
    // No warning: every direct descendant of 'GateState' is handled.
};

closed 修饰符是上下文关键字。 closed 类是隐式 abstract 的,不能与 sealedstatic 或显式 abstract 修饰符结合使用。 派生不是传递性的:封闭类的非封闭后代仍然可以在其他程序集中被派生。 为了将穷举性检查扩展到层次结构下游,也要将中间后代标记为 closed

注释

在 C# 15 预览版 5 中,运行时尚未附带 System.Runtime.CompilerServices.ClosedAttribute。 在完成之前,使用 closed 修饰符的每个项目都必须声明属性本身:

namespace System.Runtime.CompilerServices;

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class ClosedAttribute : Attribute { }

有关详细信息,请参阅语言参考中的 封闭修饰符封闭层次结构模式 ,或 功能规范。 你可以从 ClosedAttribute GitHub 存储库中的 keywords 片段项目复制本节中的示例,包括 dotnet/docs 解决方法。

内存安全

C# 15 开启了一项跨多个版本重新定义该语言内存安全性的工作。 目标是将 unsafe 上下文与实际访问非托管内存的操作关联起来,而不是与指针类型的存在关联起来。 大多数内存安全漏洞都来自这些访问操作,因此该语言使审阅者和审核员脱颖而出。

在完整的模型中,成员上的 unsafe 将其标记为 requires-unsafe:审核责任转移至调用方,调用方必须在 unsafe 上下文中使用该成员。 程序集选择加入此强制,编译器使用 System.Runtime.CompilerServices.MemorySafetyRulesAttribute 属性记录选择。 该模型还添加了一个 safe 上下文关键字,用于将 extern 成员和显式布局字段标记为安全。 这些规则共同使潜在的内存不安全性边界跨程序明确。

第一步包括指针放松。 使用 preview 语言版本进行编译时,以下操作不再需要 unsafe 上下文:

  • 声明指针类型并使用运算符获取变量 & 的地址。
  • fixed 语句,用于锁定变量。
  • stackalloc 表达式转换为指针。
  • 应用于任何非托管类型的 sizeof 运算符。

以下示例创建并固定没有上下文的 unsafe 指针:

int number = 42;
int* pointer = &number;

int[] numbers = [10, 20, 30];
fixed (int* first = numbers)
{
    // Dereferencing the pointer still requires an unsafe context.
}

访问指向内存的操作(如指针间接访问(*p)、指针成员访问(p->member)、指针元素访问(p[i])和函数指针调用仍需要 unsafe 上下文。

requires-unsafe 成员模型、程序集对更新后的内存安全规则的自愿采用,以及 safe 上下文关键字将在后续预览版中推出。

有关详细信息,请参阅语言引用或功能规范中的不安全代码、指针类型和函数指针

另见