初探 C# 15 的 Union Types

C# 15 引入的联合类型(Union Types)是一项备受期待的功能,旨在以一种类型安全的方式表示一个值可以是多种固定类型中的一种。从 .NET 11 Preview 2 开始,该功能已引入编译期支持,Preview 3 则进一步完善了 IDE 的开发体验。

官方博客的公告文章明确指出,union 关键字用于声明一个值是一组固定类型中的恰好一种 。编译器会强制要求对联合类型的变量进行穷尽性模式匹配(Exhaustive Pattern Matching),确保所有可能的类型分支都被处理,从而大大增强了代码的健壮性。

基础语法与核心概念

联合类型通过 union 关键字声明,可以直接与现有类型组合,语法非常简洁。

  • 声明与使用 :你可以直接列出允许的类型来定义一个联合。当给联合类型的变量赋值时,编译器会提供从这些具体类型到联合类型的隐式转换
cs 复制代码
// 1. 定义一些现有类型
public record class Cat(string Name);
public record class Dog(string Name);
public record class Bird(string Name);

// 2. 用一行代码声明联合类型
public union Pet(Cat, Dog, Bird);

// 3. 使用
Pet pet = new Dog("Rex"); // 隐式转换

// 4. 结合模式匹配进行穷尽性检查
string name = pet switch
{
    Dog d => d.Name,
    Cat c => c.Name,
    Bird b => b.Name,
    // 无需 default 分支,因为编译器知道已处理所有情况
};
  • 编译器生成代码 :这种简洁的声明实际上是语法糖。编译器会在后台生成一个 struct,它包含一个 object? 类型的 Value 属性以及对应每个 case 的构造函数。
cs 复制代码
// 'public union Pet(Cat, Dog, Bird);' 大致会生成如下代码
[Union]
public struct Pet : IUnion
{
    public Pet(Cat value) => Value = value;
    public Pet(Dog value) => Value = value;
    public Pet(Bird value) => Value = value;
    public object? Value { get; }
}

实际应用场景

联合类型能优雅地解决许多传统 C# 代码中难以处理的场景。

  • 处理 OneOrMore<T> 场景:当一个值可以是一个单独元素,也可以是多个元素的集合时,联合类型非常有用。你还可以在联合体内定义方法,便于使用。
cs 复制代码
public union OneOrMore<T>(T, IEnumerable<T>)
{
    public IEnumerable<T> AsEnumerable() => Value switch
    {
        T single => [single],
        IEnumerable<T> multiple => multiple,
        null => [] // 处理 null 情况
    };
}

// 使用
OneOrMore<string> tags = "dotnet";           // 使用单个值
OneOrMore<string> moreTags = new[] { "csharp", "unions" }; // 使用集合

foreach (var tag in tags.AsEnumerable()) // 统一的遍历方式
{
    Console.Write($"[{tag}]");
}
  • 实现类型安全的 Result<T>:传统上用包装类或抛出异常来处理错误,既不优雅也不安全。联合类型可以完美地表达"要么成功返回值,要么失败返回异常"的逻辑。
cs 复制代码
public union Result<T>(T, Exception);

Result<int> Divide(int a, int b)
{
    if (b == 0) return new DivideByZeroException();
    return a / b;
}

// 使用时,编译器强制你处理成功和失败两种情况
var result = Divide(10, 0);
string message = result switch
{
    int value => $"结果是 {value}",
    Exception ex => $"出错了:{ex.Message}"
};

高级特性与未来发展

联合类型的设计考虑了灵活性和性能,同时也为未来更强大的功能奠定了基础。

  • 自定义联合(避免装箱) :默认实现会对值类型进行装箱。如果对性能有极致要求,你可以通过手动为类型添加 [Union] 特性,并实现特定的 TryGetValue 模式来避免装箱。

  • 三大穷尽性检查特性 :联合类型是 C# 迈向更完备的类型系统的一部分。它与计划中的封闭类层次结构closed class)和封闭枚举closed enum)共同构成了三大穷尽性检查机制。closed class 可以限制派生类只能在同一个程序集中定义,这与联合类型的"封闭类型集"思想一致。

参考文章链接

以下是关于 C# 15 联合类型的补充参考文章,按推荐优先级整理:

1. Microsoft 官方博客(最权威)

Explore union types in C# 15

来源:.NET Blog(Microsoft Developer Blogs)

作者:Bill Wagner(C#/.NET Principal Content Developer)

链接:https://devblogs.microsoft.com/dotnet/csharp-15-union-types/

这是最核心的官方参考资料,包含:

  • Union 类型的设计理念和语法介绍

  • OneOrMore<T> 等实战示例

  • 自定义 Union 实现([Union] 特性、非装箱访问模式)

  • 与封闭层次结构(Closed hierarchies)、封闭枚举(Closed enums)的路线图关系

  • 如何在 .NET 11 Preview 中启用的详细步骤

2. 详尽技术解读(中文)

C# 15 类型系统改进:Union Types

来源:图灵课堂

链接:https://blog.tulingxueyuan.cn/tlzx/jsp/22039.html

这篇文章对官方内容进行了非常详尽的补充,包含:

  • 从实际问题出发的设计动机分析

  • Result<T> 完整实现示例

  • 手动实现 Union 模式([Union] 特性)

  • 非装箱访问模式的性能优化方案

  • 与 F# Discriminated Unions 的对比说明

适合需要深入理解实现细节的开发者。

3. Preview 3 版本动态

.NET 11 Preview 3 发布:C# 15 union 类型终补齐,Kestrel 暴增 40%

来源:博客园

链接:https://www.cnblogs.com/shenchuanchao/p/19914448/

本文补充了 Preview 3 的更新信息

  • IDE 体验完善(智能感知、重构、导航全链路可用)

  • Preview 2 vs Preview 3 的功能演进说明

  • Polyfill 代码要求(正式版将内置)

4. 概念对比与背景分析

C# 15 Unions

来源:NDepend Blog

链接:https://blog.ndepend.com/csharp-unions/

适合想了解设计决策背景的读者:

  • C# Union 与 F# Discriminated Union 的差异分析

  • 值类型装箱问题的性能讨论

  • 与开闭原则(OCP)的关系

  • Reddit 上 C# 团队成员的补充说明

快速参考汇总

文章 来源 核心价值
Explore union types in C# 15 Microsoft 官方 必读:权威语法与设计
C# 15 类型系统改进:Union Types 图灵课堂 详尽中文解读 + 实战示例
.NET 11 Preview 3 发布 博客园 Preview 3 IDE 体验更新
C# 15 Unions NDepend Blog 设计背景 + 性能分析
相关推荐
唐青枫1 小时前
Java Spring WebFlux 实战指南:用 Mono、Flux 和 WebClient 写响应式接口
java·spring
To_OC11 小时前
LC 49 字母异位词分组:想到哈希表很简单,选对 key 才是精髓
javascript·算法·leetcode
小bo波15 小时前
使用Thread子类创建线程 VS 使用Runnable接口创建线程的区别
java·多线程·thread·并发编程·runnable
SamDeepThinking15 小时前
高并发场景下,CompletableFuture与ForkJoinPool该如何取舍?
java·后端·面试
用户9385156350716 小时前
从 O(n²) 到 O(nlogn):一文读懂快速排序的“快”与“妙”
javascript·算法
To_OC17 小时前
手写快排次次翻车?别死背快排模板了,这才是面试官想听的底层逻辑
javascript·算法·排序算法
饼干哥哥18 小时前
Reddit VOC调研太慢?搭一个AI专家团队半小时洞察任何品类|以猫用饮水机为例
人工智能·算法·ai编程
张不才18 小时前
CPU 100% 了怎么办?Java 性能排障的标准化操作
java·后端
地平线开发者19 小时前
Transformer模型部署之性能优化指南
算法
地平线开发者19 小时前
人在途中:从“编译失败”到“模型可落地”——CUDA 自定义算子
算法·自动驾驶