C# 终于支持 union types 了

发布时间:2026/5/28 3:48:03

C# 终于支持 union types 了 C# 15 中的联合 unionIntrounion联合类型在 C# 中的需求一直很高现在终于要来了。从 .NET 11 Preview 2 开始C# 15 引入了 union 关键字。union 关键字声明一个值恰好是固定类型集合中的一种并且具有编译器强制执行的穷尽模式匹配。C# 的联合旨在提供原生的 C# 使用体验它们是组合现有类型的类型联合与模式匹配集成并与语言的其余部分无缝协作。Whatunion可以是几个不同类型的组合换句话说一个值只能是几种指定类型中的其中一种。--- 有穷尽的类型枚举联合体是一组相互关联的特性它们共同为 C# 提供对联合体类型的支持• 联合体类型带有[Union]特性的结构体和类会被识别为联合体类型并支持联合体行为。• 案例类型联合类型具有一组案例类型表示其内容值可以具有的类型。• 联合体行为联合体类型支持以下联合体行为• 联合体转换每个类型都存在到联合体的隐式转换。• 联合体匹配对联合体值进行模式匹配时会隐式地“解包”其内容并将模式应用于底层值Value。• 联合体穷尽性当所有类型都匹配完毕时对联合体值执行 switch 表达式是穷尽的无需回退类型。• 联合体可空性可空性分析增强了对联合体内容空状态的跟踪。• 联合体模式所有联合体类型都遵循一个基本的联合体模式但还有一些可选模式用于特定场景。• 联合体声明可以使用简写语法直接声明联合体类型。该实现方式是“预设的”——结构体声明遵循基本的联合模式并将内容存储为单个引用字段。在 .NET Preview 4 之前要使用的话还需要声明以下 polyfill 代码在 .NET 11 Preview 4 之后就不需要自己声明了已经包含在框架类库中了 https://github.com/dotnet/runtime/pull/127001namespace System.Runtime.CompilerServices; [AttributeUsage(Class | Struct, AllowMultiple false, Inherited false)] public sealed class UnionAttribute : Attribute; public interface IUnion { object? Value { get; } }SamplesGet Started举个例子public record class Cat(string Name); public record class Dog(string Name); public union Pet(Cat, Dog);这里定义了Cat和Dog两个类型然后定义了一个union这个union由Cat和Dog组成这个Petunion 就可以是Dog也可以是Cat不可以是其他的类型使用示例如下Pet pet new Cat(A); Console.WriteLine(pet switch { Cat c $Cat: {c.Name}, Dog d $Dog: {d.Name} });这里我们不需要指定_fallback 匹配因为Pet只会出现Cat/Dog这两种类型编译器也不会给出警告如果未来 Pet 新增了其他类型编译器就会给出警告The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern Xxx is not covered.union的声明会自动创建隐式转换像前面的Pet pet new Cat(A);一样编译器可以自动完成从具体类型到联合类型的隐式转换联合还比较适合用于 Result 模式示例如下public union GetUserResult(User, NotFoundError, ValidationError); return result switch { User u Ok(u), NotFoundError e NotFound(e.Message), ValidationError e BadRequest(e.Message), };反编译看一下代码Pet decompilation可以看到 union 默认是生成了一个struct并且添加了 Union Attribute 并实现了IUnion接口通过Value返回其中的对象这里可以看到如果是值类型的话就会发生装箱这对于一些对于热路径性能敏感的代码处使用默认的实现可能会存在性能问题我们也可以有自己的实现来避免发生装箱public union IntegerOrString(int, string); IntegerOrString union1 42; IntegerOrString union2 Hello; Console.WriteLine($Union1: {union1.Value}); Console.WriteLine($Union2: {union2.Value});IntegerOrStringAvoid Boxing在默认的实现中对于值类型来说会出现装箱的问题如上面的示例所示对比 union 也支持了另外一个模式来避免装箱// Non-boxing access members public bool HasValue { get { ... } } public bool TryGetValue(out Dog value) { ... }• 如果 Union 的Value不是null,HasValue应该返回true•TryGetValue仅在联合的值是给定案例类型的非空值时返回true并且如果是这样则将该值传递到方法的out参数中。避免装箱的一个示例如下[Union] public readonly struct IntegerOrStringCustomized { private readonly string? _value; private readonly int? _num null; public IntegerOrStringCustomized(int num) { _num num; } public IntegerOrStringCustomized(string value) { _value value; } public object? Value _num is null ? _value : ${_num}; public bool HasValue _value is not null _num is not null; public bool TryGetValue(out int? value) { if (_num is int intNum) { value intNum; return true; } value null; return false; } public bool TryGetValue(out string? value) { if (_value is string stringValue) { value stringValue; return true; } value null; return false; } }使用示例如下IntegerOrStringCustomized union3 42; if (union3.TryGetValue(out int? num)) { Console.WriteLine($Union3 integer value is {num}); } union3 Hello; if (union3.TryGetValue(out string? str)) { Console.WriteLine($Union3 string value is {str}); }这里的UnionAttribute 是必须的否则不会被当作 union 不能进行隐式转换会得到下面的错误// CS0029: Cannot implicitly convert type int to CSharp15Samples.IntegerOrStringCustomized另外如果要支持 switch 模式匹配的话必须要有一个公开的object? Value { get; }属性不然编译器会报错// Missing compiler required member IntegerOrStringCustomized.Value使用如下var stringValue union3 switch { int i i.ToString(), string s s }; Console.WriteLine(stringValue);反编译看一下实现会是什么样的More目前 union 还在不断的完善包括 union 本身的优化如IUnionMembers等以及框架的支持比如 JSON 序列化和 ASP.NET Core 的支持期待越来越完善好用。References• https://github.com/dotnet/csharplang/blob/main/proposals/unions.md• https://andrewlock.net/exploring-the-dotnet-11-preview-2-dotnet-gets-union-types• https://github.com/dotnet/runtime/pull/127001• https://github.com/WeihanLi/SamplesInPractice/blob/main/Net11Samples/CSharp15Samples/UnionSample.cs

相关新闻