当前位置: 首页 > news >正文

C# 终于支持 union types 了

C# 15 中的联合 union

Intro

union联合类型在 C# 中的需求一直很高,现在终于要来了。从 .NET 11 Preview 2 开始,C# 15 引入了 union 关键字。union 关键字声明一个值恰好是固定类型集合中的一种,并且具有编译器强制执行的穷尽模式匹配。C# 的联合旨在提供原生的 C# 使用体验:它们是组合现有类型的类型联合,与模式匹配集成,并与语言的其余部分无缝协作。

What

union可以是几个不同类型的组合,换句话说,一个值只能是几种指定类型中的其中一种。

--- 有穷尽的类型枚举

联合体是一组相互关联的特性,它们共同为 C# 提供对联合体类型的支持:

  • • 联合体类型:带有[Union]特性的结构体和类会被识别为联合体类型,并支持联合体行为。

  • • 案例类型:联合类型具有一组案例类型,表示其内容值可以具有的类型。

  • • 联合体行为:联合体类型支持以下联合体行为:

    • • 联合体转换:每个类型都存在到联合体的隐式转换。

    • • 联合体匹配:对联合体值进行模式匹配时,会隐式地“解包”其内容,并将模式应用于底层值Value

    • • 联合体穷尽性:当所有类型都匹配完毕时,对联合体值执行 switch 表达式是穷尽的,无需回退类型。

    • • 联合体可空性:可空性分析增强了对联合体内容空状态的跟踪。

  • • 联合体模式:所有联合体类型都遵循一个基本的联合体模式,但还有一些可选模式用于特定场景。

  • • 联合体声明:可以使用简写语法直接声明联合体类型。该实现方式是“预设的”——结构体声明遵循基本的联合模式,并将内容存储为单个引用字段。

在 .NET Preview 4 之前要使用的话还需要声明以下 polyfill 代码,在 .NET 11 Preview 4 之后就不需要自己声明了,已经包含在框架类库中了 https://github.com/dotnet/runtime/pull/127001

namespace System.Runtime.CompilerServices; [AttributeUsage(Class | Struct, AllowMultiple = false, Inherited = false)] public sealed class UnionAttribute : Attribute; public interface IUnion { object? Value { get; } }

Samples

Get Started

举个例子:

public record class Cat(string Name); public record class Dog(string Name); public union Pet(Cat, Dog);

这里定义了CatDog两个类型,然后定义了一个union这个unionCatDog组成,这个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}");

`IntegerOrString`

Avoid 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

http://www.jsqmd.com/news/900773/

相关文章:

  • NestJS项目接口权限怎么管理?结合Swagger文档清晰展示JWT守卫与角色控制
  • 从普通到Low ESR:手把手教你读懂铝电解电容规格书里的‘损耗角’与ESR换算
  • 3分钟掌握:tchMaterial-parser电子课本下载工具完整使用指南
  • 数据仓库实战:当Hive表插错数据后,我是如何用‘重写’而不是‘删除’来救场的
  • 【网安-Web渗透测试-免杀系列】PowerShell免杀
  • 别再死记硬背公式了!用Python+Matplotlib手把手教你画滤波器的Bode图(附代码)
  • 用Python手把手复现FOIL算法:从家庭关系图谱到知识推理的完整实战
  • Cell-Free Massive MIMO硬件损伤分析与优化策略
  • 烤火罩在潮湿环境容易发霉吗 新 E 选品牌源头厂家说明
  • 【Xiaomi】Xiaomi 17 Max发布就讲透
  • 量子张量网络在BEC模拟中的高效应用
  • 从零开始:构建你的缠论量化交易系统 - Chanlun-Pro实战指南
  • 侈品级不锈钢彩色板应用技术标准:从选材、工艺到验收的完整规范
  • 算法:图的存储与遍历,最小生成树(Prim算法,kruskal算法)
  • 别再傻傻分不清!一文搞懂CPU、GPU、NPU、MCU、DSP、FPGA、SOC,嵌入式选型不踩坑
  • 别只让LED闪了!基于STM32CubeMX的HAL库,教你玩转GPIO输入输出与硬件抽象层设计
  • 推荐题目:洛谷 P5730 【深基5.例10】显示屏
  • 别再找第三方工具了!用Windows自带的DISM命令,5分钟给Win10家庭版装上组策略编辑器
  • 在OpenClaw中配置Taotoken作为后端AI供应商的详细步骤
  • Cortex-M3/M4调试系统设计:TPIU与CoreSight Funnel应用
  • ROCK5B新手避坑指南:用BalenaEtcher给NVMe刷Debian11,从驱动安装到首次登录的完整流程
  • 从彩虹猫到MBR:一次MEMZ病毒‘事故’后,我搞懂了Windows引导修复的几种方法
  • [智能体-119]:LangChain 生态工具详解
  • 2026年4月花灯供货商怎么选,景区灯会/大型户外花灯/天幕花灯/春节国潮花灯/春节花灯/巡游花灯,花灯定做厂家推荐分析 - 品牌推荐师
  • 2026支持百度AI优化的GEO服务商测评:服务优质响应高效
  • 2026年4月市场优秀的混合机直销厂家哪家可靠,链盘管链输送机/吨袋无尘拆包机/双锥混合机,混合机企业哪家靠谱 - 品牌推荐师
  • SARscape版本升级实战:5.3到5.6.2,那些官方没细说的数据导入与DEM处理变化
  • 别再死磕梯度下降了!用Python手把手教你实现遗传算法解决旅行商问题
  • 深入浅出 LoongSuite Python Agent:让你的 AI 应用「透明化」(上篇)
  • 数据分析入门:手把手教你用Python爬取直播数据并做简单可视化