news 2026/9/17 9:33:08

在 dotnet/runtime 中为新增公共 API 更新参考程序集(Reference Assembly)的完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
在 dotnet/runtime 中为新增公共 API 更新参考程序集(Reference Assembly)的完整指南

在 dotnet/runtime 中为新增公共 API 更新参考程序集(Reference Assembly)的完整指南

【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime

本文以 updating-ref-source.md 为核心骨架,系统讲解在 .NET runtime 仓库中,当向实现程序集(implementation assembly)添加新的公共 API后,如何正确更新对应参考程序集(reference assembly,即ref目录下的 API 骨架源码)。文章覆盖大多数库的标准流程、System.Runtime的特殊处理、Full Facade 程序集与 .NETFramework Facade 程序集的专属命令,并结合仓库内的 ref 目录结构与源码佐证每一步的底层原理,帮助读者在完成 API Review 后,安全、无漂移地把新 API 同步进参考程序集并落地测试。

为什么需要更新参考程序集

在 .NET 的编译与运行时体系中,同一份公共 API 存在两套源码:一套位于src目录(实现程序集,包含真实的方法体与内部实现),另一套位于ref目录(参考程序集,只保留公共 API 的签名骨架,方法体为空或抛出异常)。以仓库中的 System.Collections 为例,其目录结构清晰地体现了这一分离:

src/libraries/System.Collections/ ├── ref/ # 参考程序集:System.Collections.cs、System.Collections.Forwards.cs ├── src/ # 实现程序集 └── tests/ # 测试

参考程序集的作用是:对外发布一份稳定、不含实现细节的 API 契约,编译器在编译用户代码时针对这份契约解析签名,从而允许运行时自由调整内部实现而不破坏二进制兼容。因此,每当实现程序集新增公共类型或成员,都必须同步更新ref目录下的对应源文件,否则新 API 无法被外部消费,ApiCompat 校验也会失败。

需要注意的是:该更新流程的前提是 API 已经通过了 API Review,即新 API 的命名与签名已获得正式评审认可;本文不涉及 API 评审本身,只解决"评审通过之后如何落地"的问题。

大多数库的标准更新流程

对于src/libraries下绝大多数程序集,更新参考程序集遵循以下四步流程。

步骤 1:实现 API 并构建实现程序集

先在实现程序集中完成新 API 的代码编写,然后按照 构建单个库 的指引执行构建。

一个常见的坑是:新增公共类型时,构建可能直接报TypeMustExist错误。这是因为参考程序集还缺少该类型,而 ApiCompat(API 兼容性校验)在实现程序集中找不到对应契约时便拒绝继续。此时需要临时禁用 ApiCompat 的程序集校验来打破这个死锁:

dotnet build /p:ApiCompatValidateAssemblies=false

通过该参数跳过校验后,即可进入下一步生成参考程序集源码。

步骤 2:用 GenAPI 工具重新生成参考程序集源码

src目录(即src/libraries/<程序集>/src)执行以下命令,运行 GenAPI 工具:

dotnet msbuild /t:GenerateReferenceAssemblySource

GenAPI 会扫描实现程序集中的公共 API,并重新生成ref目录下的参考源码。需要特别强调两点:

  • GenAPI 的输出往往存在大量噪音。仓库已知存在 GenAPI 生成大量不相关差异的问题(见 dotnet/runtime 仓库 issue #100843),因此生成后通常需要手工修正:筛掉无关改动,只保留与本次新 API 相关的差异(例如可以忽略某些被移除的 attribute)。
  • 不建议完全手工编辑参考源文件。完全手写会导致参考程序集与 GenAPI 工具的生成结果产生漂移(drift),使后续更新变得难以 diff,并带来"参考程序集与运行时程序集不一致"的风险。正确姿势是以 GenAPI 输出为基础再做最小手工修正。

步骤 3:构建参考程序集

生成或修正完源码后,进入ref目录,构建参考程序集项目。以仓库中的 System.Collections/ref/System.Collections.csproj 为参考,参考程序集项目本质上是一个极简的类库项目:

<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>$(NetCoreAppCurrent)</TargetFramework> </PropertyGroup> <ItemGroup> <Compile Include="System.Collections.cs" /> <Compile Include="System.Collections.Forwards.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\System.Runtime\ref\System.Runtime.csproj" /> </ItemGroup> </Project>

从中可以读出两个重要事实:参考程序集项目把System.Collections.cs与类型转发文件System.Collections.Forwards.cs一并编译;并且它通过ProjectReference依赖 System.Runtime 的参考程序集——这也解释了为什么System.Runtime在整个 ref 体系中处于最底层、更新时需特殊对待(见下文)。

步骤 4:添加并运行测试

新 API 不仅需要参考程序集与实现程序集,还需要配套的测试(位于程序集目录下的tests/文件夹)。添加测试后执行构建与测试,确认签名契约、实现与测试三者一致。

补充:已手工添加 API 后的重新生成

注意:如果你在生成参考源之前,已经手工把新 API 加进了参考源文件,那么在构建完实现程序集后重新执行GenerateReferenceAssemblySource,GenAPI 会将该 API全限定化(fully qualified)并归位到正确的排序位置。这种情况下命令从ref目录执行即可:

# 在 ref 目录下执行 dotnet msbuild /t:GenerateReferenceAssemblySource

也就是说,GenerateReferenceAssemblySource这个 target 在srcref两个目录下都可运行,只是适用场景不同:src下运行是"从实现生成参考源",ref下运行是"基于已有的参考源做规范整理"。

System.Runtime 的特殊流程

System.Runtime是整个 .NET 类库的地基参考程序集,且它直接依赖System.Private.CoreLib中的类型,因此不能直接套用通用流程,需要先构建底层再生成。

适用场景

这套流程同样适用于依赖 System.Private.CoreLib 变更的少数特殊程序集,例如System.Memory这类 partial facade(部分门面)程序集——它们只转发System.Private.CoreLib中已定义类型的部分成员。

操作步骤

  1. System.Runtime/src目录执行以下命令(注意多了--no-incremental,确保不走增量缓存、完整重生成):
dotnet build --no-incremental /t:GenerateReferenceAssemblySource
  1. 过滤无关变更:由于System.Runtime参考源体量巨大,生成结果中会混入大量与本次改动无关的差异(例如某些 attribute 被移除)。这一步需要手工把无关差异全部剔除,只保留与你新增 API 相关的部分。文档明确指出:"Generally, this step is not required for other reference assemblies"——即对其他参考程序集而言通常不需要这么麻烦,唯独System.Runtime必须经历这一轮清理。

仓库中可以看到System.Runtime的 src 目录下存在 System.Runtime.Typeforwards.cs,说明该程序集的类型转发也是单独文件管理,与 System.Runtime/ref 下的参考源配合维护。

Full Facade 程序集的专属命令

仓库中有一类实现程序集本质上是另一程序集的full facade(完整门面),自身几乎不承载实现,却在参考程序集中定义类型。典型例子包括System.Runtime.Serialization.JsonSystem.Xml.XDocument等。

对于这类程序集,直接生成参考源会丢失类型转发关系,必须显式让 GenAPI 跟随类型转发。对应的命令为:

dotnet msbuild /t:GenerateReferenceAssemblySource /p:GenAPIFollowTypeForwards=true

与标准命令相比,多出的/p:GenAPIFollowTypeForwards=true属性会指导 GenAPI 在遇到类型转发时沿着 TypeForwardedTo 继续展开,从而把被转发类型的契约也纳入参考源生成范围,保证门面程序集暴露出的 API 面完整。

.NETFramework Facade 程序集:手工补充类型转发

还有一类更特殊的程序集:它们在 .NETStandard 与 .NETCore 中定义类型,但在 .NETFramework 目标上只是facade(门面),需要把类型转发到 .NETFramework 中既有类型所在的位置。这种情况下,无法依赖 GenAPI 自动完成,必须手工为 .NETFramework 构建下的参考程序集添加类型转发

具体要求如下:

  • 对兼容的 .NETStandard 参考程序集中的每一个类型,只要该类型在 .NETFramework 中已存在,就必须为其添加TypeForwardedTo
  • 而对于那些在 .NETFramework 参考程序集中直接定义的类型,则应当被抽取到**共享源文件(shared source file)**中,供各目标框架复用,避免重复定义。

仓库中 System.Collections/ref/System.Collections.Forwards.cs 就是这种模式的具体呈现,其内容非常简短,本质上是带强约束注释的转发声明:

// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // ------------------------------------------------------------------------------ // Changes to this file must follow the https://aka.ms/api-review process. // ------------------------------------------------------------------------------ [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.ObjectModel.ReadOnlySet<>))]

注意文件头部的注释:"Changes to this file must follow the https://aka.ms/api-review process."——任何对类型转发文件的改动都必须走 API Review 流程,这与本文开头"先评审、后更新"的总体纪律完全一致。同时在 System.Collections/ref/System.Collections.csproj 中,System.Collections.Forwards.cs与主参考源System.Collections.cs一起被<Compile Include>纳入编译,印证了"转发文件 + 契约文件"并行维护的项目组织方式。

常见问题与最佳实践小结

场景命令 / 操作关键点
大多数库(标准流程)src目录执行dotnet msbuild /t:GenerateReferenceAssemblySource新增公共类型时先以/p:ApiCompatValidateAssemblies=false打破TypeMustExist死锁
已在 ref 中手工添加过 APIref目录执行同一 targetGenAPI 会做全限定化并重新排序
System.Runtime(及依赖 CoreLib 的 partial facade)System.Runtime/src执行dotnet build --no-incremental /t:GenerateReferenceAssemblySource必须过滤无关差异,只保留本次改动
Full Facade 程序集(如 System.Runtime.Serialization.Json、System.Xml.XDocument)dotnet msbuild /t:GenerateReferenceAssemblySource /p:GenAPIFollowTypeForwards=true显式跟随类型转发,保证契约完整
.NETFramework Facade手工为 .NETFramework 参考程序集添加TypeForwardedTo;框架内定义的类型抽入共享源文件需走 API Review;改动遵守 ref 文件头部的评审注释约定

贯穿全文的三条纪律,值得在每一次 API 落地时自查:

  1. 先评审、再更新:新增公共 API 前必须先完成 API Review,参考程序集的任何变更都受此约束(参考 ref 源文件 中的文件头注释约定)。
  2. 以 GenAPI 输出为基底,而非全手工编写:手工硬编码会导致参考程序集与工具生成结果漂移,增加后续维护风险;生成后的人工修正应聚焦于过滤噪音、保留本次改动。
  3. 分层处理,特殊程序集走特殊流程System.Runtime需要--no-incremental全量重建并人工清理;Full Facade 需要GenAPIFollowTypeForwards=true;.NETFramework Facade 则需要手工维护类型转发。识别你正在处理的程序集属于哪一类,直接决定命令与工作量。

遵循上述流程,即可保证新增公共 API 在实现程序集、参考程序集与测试三端保持一致,让 API 契约稳定、可被编译器正确解析,也避免了参考程序集与运行时程序集之间的漂移风险。

【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 9:31:11

Spring Boot文件上传下载实战与优化策略

1. 文件传输在现代Web应用中的核心地位文件上传与下载功能看似基础&#xff0c;实则是现代Web应用中最高频使用的功能模块之一。从社交媒体平台的图片分享到企业OA系统的文档流转&#xff0c;从在线教育平台的课件分发到医疗系统的影像传输&#xff0c;文件交互能力直接影响着用…

作者头像 李华
网站建设 2026/9/17 9:31:07

Ubuntu / WSL 安装pipx uv 管理项目

一、pipx管理工具 1.1 安装 pipx 在 Ubuntu / WSL 上可以用两种方式安装&#xff0c;推荐第二种&#xff08;官方脚本&#xff09;或第三种&#xff08;pip 安装最新版本并自动配置 PATH&#xff09;。 1.1.1、apt 安装&#xff08;最快&#xff0c;但版本往往偏旧&#xff…

作者头像 李华
网站建设 2026/9/17 9:30:21

把Scratch逼成游戏引擎?三个月实战复盘:性能优化与架构设计

经常有人问我&#xff0c;都2025年了&#xff0c;为什么还有人要把Scratch逼成游戏引擎&#xff1f;不瞒你说&#xff0c;我一度也回答不上来。但当我真正动手&#xff0c;把一款正经的空战射击游戏塞进这个“教小孩拖积木”的工具里&#xff0c;还稳定跑在接近60帧时&#xff…

作者头像 李华
网站建设 2026/9/17 9:30:11

数据库加密四大方案:TDE、列加密、应用层与存储层实战对比

1. 数据库数据加密不是“选个插件就完事”&#xff0c;而是分层防御的系统工程数据库数据加密这件事&#xff0c;我带过十几支企业级开发团队&#xff0c;从金融核心账务系统到政务人口库&#xff0c;踩过的坑比写过的SQL还多。很多人一上来就问&#xff1a;“TDE和应用层加密哪…

作者头像 李华
网站建设 2026/9/17 9:27:34

SpringBoot+Vue构建健康管理系统的全栈实践

1. 项目概述&#xff1a;当健康管理遇上全栈开发去年参与某健康科技公司系统重构时&#xff0c;我接手了一个与"123健康管理系统"高度相似的项目。这类系统本质上是通过数字化手段实现健康数据的采集、分析和干预&#xff0c;而SpringBootVue的技术组合恰好能完美支撑…

作者头像 李华