尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
c#联合类型,解决响应接口返回结果有多重变体
一、C# 没有联合类型但我们可以“搭”一个先对齐概念。联合类型Union Type的意思是一个值只能是预先定义好的几种形态之一。比如“创建订单”结果无非三种——成功、参数校验失败、业务规则拒绝。F#、Swift 这类语言有原生的 discriminated unionC# 至今没有这个语法糖C# 12 起有实验性的UnionAttribute/IUnion尝试但还不是一等公民语法。不过用 record 继承加模式匹配完全能模拟出一个够用的版本// 基类代表创建订单这个操作所有可能的结果abstract 防止外部直接实例化public abstract record CreateOrderResult; // 形态一成功携带订单号和金额 public sealed record OrderCreated(Guid OrderId, decimal Total) : CreateOrderResult; // 形态二参数校验失败 public sealed record OrderValidationFailed(string Message) : CreateOrderResult; // 形态三业务规则拒绝比如超出信用额度 public sealed record OrderRejected(string Reason) : CreateOrderResult;派生类型全部sealed应用自己控制谁能继承——这套结构就是“约定上的封闭集合”。但要说清楚它不是编译器强制的封闭联合别人照样能偷偷继承一个新类型出来所以兜底逻辑不能省后面会讲。二、让 System.Text.Json 认识这套类型光有类型层次不够序列化器得知道“这段 JSON 对应哪个派生类型”。.NET 7 之后System.Text.Json 原生支持多态序列化两个特性搞定using System.Text.Json.Serialization; // 在基类上注册所有派生类型并给每个类型起一个稳定的 JSON 判别名 [JsonPolymorphic(TypeDiscriminatorPropertyName kind)] [JsonDerivedType(typeof(OrderCreated), order_created)] [JsonDerivedType(typeof(OrderValidationFailed), validation_failed)] [JsonDerivedType(typeof(OrderRejected), order_rejected)] public abstract record CreateOrderResult; public sealed record OrderCreated(Guid OrderId, decimal Total) : CreateOrderResult; public sealed record OrderValidationFailed(string Message) : CreateOrderResult; public sealed record OrderRejected(string Reason) : CreateOrderResult;序列化出来的 JSON 长这样kind字段就是类型判别符{ kind: order_created, orderId: d8b2c01e-ff31-4d0a-a1e2-3d6c6c6e8a10, total: 149.99 }被拒绝时则是另一种形状{ kind: order_rejected, reason: 订单超出客户信用额度。 }判别值要写order_created、order_rejected这种业务语义别把 CLR 类型名直接暴露出去——类型名是实现细节哪天重构改名客户端契约就跟着崩了。另外命名风格要统一要么全 snake_case要么全 camelCase别created和validation_failed混着来。三、我踩过最狠的坑声明类型不对kind 直接消失这个坑值得单独拎出来讲。多态序列化有个前提序列化时使用的声明类型必须是注册过多态信息的基类型。// 反面教材声明类型是派生类序列化结果里不会有 kind 字段 OrderCreated created new(Guid.NewGuid(), 149.99m); string bad JsonSerializer.Serialize(created); // 正确姿势用基类型变量装着派生对象 CreateOrderResult good new OrderCreated(Guid.NewGuid(), 149.99m); string ok JsonSerializer.Serialize(good); // 这才有 kind: order_created // 也可以显式指定泛型参数效果一样 string ok2 JsonSerializer.SerializeCreateOrderResult(created);你发现没有这跟直觉有点拧对象明明是OrderCreated却必须“装”在基类型的变量里序列化。我第一次遇到时对着日志找了快半小时。记住一句话想多态序列化时用的声明类型就得是基类。四、在 Minimal API 里落地两层各干各的先把请求类型和服务接口定下来public sealed record CreateOrderRequest(string CustomerId, decimal Total); public interface IOrderService { TaskCreateOrderResult CreateAsync( CreateOrderRequest request, CancellationToken cancellationToken); }端点只做一件事——把应用层算出的结果翻译成 HTTP 响应var builder WebApplication.CreateBuilder(args); var app builder.Build(); app.MapPost(/orders, async ( CreateOrderRequest request, IOrderService orderService, CancellationToken ct) { // 应用层负责算出是哪种结果完全不知道 HTTP 的存在 CreateOrderResult result await orderService.CreateAsync(request, ct); // API 层负责把结果映射成对应的状态码 return result switch { OrderCreated created Results.Created($/orders/{created.OrderId}, created), OrderValidationFailed f Results.BadRequest(f), OrderRejected rejected Results.Conflict(rejected), _ Results.Problem() }; }); app.Run();这里有个设计上的关键OrderRejected这种领域结果里从头到尾不该出现IResult、StatusCodes这些字眼。业务拒绝和 409 状态码是相关但不等价的两件事——同一个OrderRejected到了 gRPC 端点、消息消费者或 CLI 里可能该记日志、该触发补偿而不是拼 HTTP 响应。领域模型保持干净传输层的决策留在 API 边界这个结果对象拿去给别的入口复用一点问题没有。另外那个_ Results.Problem()兜底分支虽然理论上不该被触发派生类型 sealed、约定封闭但真被触发说明有人违反了约定。建议在这里记一条警告日志而不是静默返回 500——否则问题会被掩盖。五、反序列化好用但别裸奔读队列消息、处理落库的历史 JSON 时这套多态配置同样生效string json { kind: order_rejected, reason: 信用额度超限。 } ; // 基类注册过派生类型序列化器会按 kind 自动构造对应的派生记录 CreateOrderResult? result JsonSerializer.DeserializeCreateOrderResult(json);但两句大实话必须说一是反序列化成功不等于业务合法。注册JsonDerivedType只是告诉序列化器“有哪些类型”JSON 里的值对不对、金额是不是负数它一概不管业务校验一行都不能省。二是外部传入的 payload 要做对抗测试。缺失 kind、未知判别值、字段类型乱写——默认配置下缺失判别符或未知判别值都会抛JsonException这其实是好事最怕的是它悄悄变成一个“合法”的业务结果。如果你显式配置了JsonUnknownDerivedTypeHandling.FallBackToBaseType之类的回退策略那就更要想清楚回退到基类之后业务层能不能识别出这是一个“来路不明”的结果。六、OpenAPI 文档代码对了契约未必对还有个容易翻车的盲区序列化行为正确不代表生成的 OpenAPI 文档就自动把每种形态描述清楚了。一份合格的联合类型契约至少要说清楚有哪几种 JSON 形态、kind 的取值、每种结果对应的状态码以及客户端遇到未知 kind 时该怎么办。不同版本的 ASP.NET Core 和 OpenAPI 工具链对多态类型的 schema 生成能力参差不齐能不能产出带 discriminator mapping 的 oneOf 结构别想当然打开生成的文档亲自核对。要发布 SDK 或给其他团队生成强类型客户端这一步绝对省不得。七、什么时候别用这招说句公道话联合类型不是银弹。如果每种响应结构基本一致、只有一两个可选字段不同老老实实用单个 DTO 加可空属性更省心。联合类型真正发光的场景是结果集合小而封闭、不同结果携带不同数据、调用方需要显式分支。反过来为鸡毛蒜皮的变体都建类型枚举出十几种 Result那是在制造新的混乱。说白了接口契约的好坏从来不在于返回了多少字段而在于调用方能不能一眼看懂“可能发生什么”。把结果的每一种可能摆上台面让编译器和序列化器替你把关这才是对使用者最基本的尊重。码字不易如果您觉得我的文章对您有帮助的话烦请您打赏一元我买瓶水喝您的支持将是我继续坚持分享的无限动力谢谢
RELATED

相关推荐

单片机毕设项目:基于 STM32 的烘焙工作室烟雾温湿度监测联动排风系统设计 基于物联网的老旧居民楼室内烟雾与空气安全监测系统设计(030122)

单片机毕设项目:基于 STM32 的烘焙工作室烟雾温湿度监测联动排风系统设计 基于物联网的老旧居民楼室内烟雾与空气安全监测系统设计(030122)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

📅 2026/10/11 11:41:26
抖音快手点赞任务平台源码解析:任务状态机、结算防重与APP打包上线指南

抖音快手点赞任务平台源码解析:任务状态机、结算防重与APP打包上线指南

简介:这是一套面向短视频任务平台运营者与二次开发者的完整源码包,聚焦抖音、快手、火山视频的点赞任务场景,适合具备PHP基础、希望快速搭建或二次开发运营平台的开发者与创业者。压缩包共约2000个文件,整体72.82MB,以…

📅 2026/10/11 11:41:26
Portabase API v1 开发指南:用 x-api-key 实现数据库备份流程完全自动化

Portabase API v1 开发指南:用 x-api-key 实现数据库备份流程完全自动化

【免费下载链接】portabase Portabase - Database backup & restore tool for PostgreSQL, MySQL, MsSQL, MariaDB, Firebird SQL, SQLite, MongoDB, Redis and Docker Volume 项目地址: https://gitcode.com/gh_mirrors/por/portabase 点击查看 免费下载 Por…

📅 2026/10/11 11:41:26
MORE NEWS

更多资讯

📰

AI及学术网址导航:用JSON配置驱动静态导航页的完整实践

简介:面向AI应用开发者和学术研究者的项目源码包,将腾讯IMA、Kimi.ai、Deepseek、智谱清言、秘塔、豆包、通义千问、Elicit等主流AI工具,与arXiv、谷歌学术镜像、百度学术、专知、Web of Science、HimmPat、Patentics、Global Dossier等学术及…

📰

人工智能技术介绍PPT怎么讲?一条主线三层拆解,避开五个坑

简介:这份《人工智能技术介绍.ppt》面向零基础或初入门的AI学习者,系统梳理神经网络与深度学习的核心概念,帮助读者建立从理论到实践的完整认知框架。内容涵盖神经网络基本构造、神经元节点与激活函数、万能近似定理、Widrow-Hoff学习规则及梯…

📰

NBU备份Oracle完整配置指南:从策略到恢复的避坑实践

简介:这份文档面向需要为企业级环境搭建 Oracle 数据库备份体系的运维工程师与 DBA,围绕 NetBackup(NBU)8.3.0.2 与 Oracle 11.2.0.4 的组合,系统梳理从客户端代理安装到备份策略落地的完整配置思路。资源包内仅含 1 个…

📰

MySQL学生信息管理系统课程设计:从建库到事务的避坑指南

简介:本资源为基于MySQL的数据库课程设计学生信息管理系统完整报告,面向高校计算机相关专业学生及数据库初学者,帮助读者将关系数据库理论转化为实际开发能力,掌握Java与MySQL结合开发数据库应用的关键技术。压缩包内共1个PDF文件…

📰

微信聊天记录本地导出:SQLite解密、Protobuf解析与三格式生成

简介:这是一套面向微信开发者与个人数据管理者的微信聊天记录提取与分析工具集,解决日常聊天数据长期保存、多格式导出及深度统计分析的痛点。资源包含238个文件,主体为94个Python脚本(实现备份解析、HTML/Word/CSV转换及年度报告…

📰

Happier代码审查完全指南:在手机上逐行审查AI Agent生成的Diff

【免费下载链接】happier Web, Desktop & Mobile client and orchestrator for Codex, Claude Code, OpenCode, Pi, Cursor, Grok, Antigravity, Kimi, Augment Code, Qwen, fully end-to-end encrypted 项目地址: https://gitcode.com/gh_mirrors/hap/happier …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬