2026 年 11 月 10 日,微软正式发布(GA).NET 11,C# 15 同步交付,作为面向 net11.0 项目的默认语言版本。.NET 11 属于标准期限支持(STS)版本,官方支持周期为两年(至 2028 年 11 月 14 日)。C# 15 聚焦类型系统表达力与编码效率,正式引入原生联合类型(Union types)与闭合继承体系(Closed hierarchies),增强编译期穷尽性检查;集合表达式支持传入构造参数;扩展机制补齐索引器;控制流增加带标签的跳转语句。除处于预览阶段的内存安全特性外,其余功能均在 .NET 11 SDK 与 Visual Studio 2026 中默认可用。笔者将结合官方规范,解析核心语言特性及工程落地建议。
1. 原生联合类型(Union types)
联合类型用于表示取值限定在固定分支类型(case types)之内的场景。以往开发者处理多状态返回值时,常使用 object 或无逻辑的抽象基类,调用方容易遗漏某些状态分支的处理。
C# 15 引入 union 关键字声明联合类型。编译器自动提供各分支类型到联合类型的隐式转换,并在 switch 表达式中强制执行穷尽性检查(exhaustiveness checking):
public sealed record Product(int Id, string Name);
public sealed record ProductNotFound(int Id);
public union ProductLookup(Product, ProductNotFound);
ProductLookup result = new Product(42, "Mechanical keyboard");
string message = result switch
{
Product product => $"Found {product.Name}",
ProductNotFound missing => $"No product with ID {missing.Id}",
};
当 switch 分支未覆盖全部 case 类型时,编译器会给出诊断警告,避免遗漏边界分支。
2. 闭合继承体系(Closed hierarchies)
面向对象继承中,常规基类无法限制外部派生,导致 switch 匹配派生类时必须编写防御性的 default 分支。
C# 15 引入 closed 修饰符。标记为 closed 的类只允许在当前声明程序集内派生,在编译期固定直接子类集合。编译器由此掌握完整派生信息,进行模式匹配时无需 default 分支即可验证穷尽性。
public closed record class GateState;
public record class Closed : GateState;
public record class Open(float Percent) : GateState;
string Describe(GateState state) => state switch
{
Closed => "closed",
Open(var percent) => $"{percent}% open",
// 编译器已知直接派生类均已处理,无需 default 分支
};
closed 语法规则:
- closed 属于上下文关键字。
- closed 类隐式具备 abstract 属性,不能与 sealed、static 或显式 abstract 共同修饰。
- 闭合约束不具备传递性。外部程序集引用非 closed 直接派生类仍可继续派生;若要维持深层继承的穷尽性,中间派生类也需声明为 closed。
3. 集合表达式参数(Collection expression arguments)
C# 12 引入的集合表达式([...])提供了统一声明语法,但此前无法向底层集合构造函数传参。初始化 HashSet 比较器或指定 List 容量时,开发者不得不退回传统构造调用。
C# 15 允许在集合表达式首位使用 with(...) 元素,直接向底层集合构造函数传递参数:
string[] values = ["one", "two", "three"];
// 传递 capacity 参数
List<string> names = [with(capacity: values.Length * 2), .. values];
// 传递比较器参数
HashSet<string> set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"];
该特性在保留展开操作符(..)便利性的同时,兼顾了容量预分配与集合比较行为定制。
4. 扩展索引器(Extension indexers)
C# 14 引入了基于 extension 块的扩展属性与扩展方法。C# 15 进一步扩展该语法块,支持定义扩展索引器(Extension indexers)。
索引器属于实例成员,因此定义扩展索引器的 extension 块必须声明具名的接收者参数(named receiver parameter):
public static class SequenceIndexer
{
extension(IEnumerable<int> sequence)
{
public int this[int index] => sequence.ElementAt(index);
}
}
IEnumerable<int> numbers = Enumerable.Range(1, 10);
int third = numbers[2];
通过扩展索引器,第三方类型与接口能够直接支持索引访问语法。
5. 带标签的 break 与 continue(Labeled break and continue)
多层嵌套循环或嵌套 switch 结构中,跳出或继续外层循环通常需要引入布尔标记变量,或者使用 goto 语句跳转。这种写法增加了状态管理的复杂度。
C# 15 支持在外层循环或 switch 语句前放置标签,内层语句可通过 break 或 continue 直接指向目标标签:
outer: for (int row = 0; row < grid.Height; row++)
{
for (int column = 0; column < grid.Width; column++)
{
if (grid[row, column].IsBlocked)
{
continue outer; // 跳至外层循环下一次迭代
}
if (grid[row, column].IsGoal)
{
break outer; // 直接跳出外层循环
}
}
}
未指定标签的跳转语句维持原有语义,仅作用于最内层结构。开发工具同步引入 IDE0410 诊断规则,辅助重构布尔标志与 goto 逻辑。
6. 内存安全演进(Memory safety - 预览特性)
C# 15 启动了跨版本内存安全规范演进,核心思路是将 unsafe 上下文约束从指针类型存在转移到具体访问非托管内存的操作上。
在第一阶段的指针规则放宽中,声明指针类型与 & 取地址、fixed 语句、stackalloc 表达式转指针、sizeof 运算符无需 unsafe 上下文。
真正访问非托管内存的操作(指针解引用 *p、成员访问 p->field、元素访问 p[i] 及函数指针调用)仍强制要求处于 unsafe 上下文:
int number = 42;
int* pointer = &number; // 声明与取地址无需 unsafe
int[] numbers = [10, 20, 30];
fixed (int* first = numbers) // fixed 语句无需 unsafe
{
// 解引用指针仍需在 unsafe 上下文中进行
}
此外,C# 15 增加了 unsafe(expression) 单表达式语法,允许在字段初始化器中执行不安全调用;增加了 safe 上下文关键字以声明安全契约。体验该特性需配置 <LangVersion>preview</LangVersion>,并按需开启 updated-memory-safety-rules 特性开关。
升级建议
| 谁该升 | 何时升 | 注意事项 |
|---|---|---|
| 新建微服务与云原生应用团队 | 2026 年第四季度即刻升级 | 需在 CI 环境配置 .NET 11 SDK 构建镜像;领域模型可采用联合类型与闭合继承。 |
| 现有 .NET 9 / .NET 10 生产服务团队 | 随 2026 Q4 至 2027 Q1 迭代窗口平稳升级 | .NET 11 属于 STS 标准期限支持版本,支持至 2028 年 11 月 14 日;需关注缺少 switch 分支给出的未穷尽警告。 |
| 通用公共类库与开源 SDK 维护者 | 2027 年第一季度规划多版本适配 | 公开接口慎用 closed 修饰符,避免限制外部调用方派生;利用扩展索引器优化既有接口调用。 |
| 深度依赖 LTS 版本的企业级核心系统 | 建议等待下一个 LTS 或仅在开发分支演练 | .NET 11 非 LTS 版本;可在分支上引入 IDE0410 规则清理深层循环跳转,结合维护周期评估生产切换。 |