Rx.NET 7.0 已经正式发布了。这个版本的目标非常清晰:进一步压缩 Windows 应用的部署体积。具体做法是把 WPF、Windows Forms、UWP 和 Windows Runtime 这些集成功能从 System.Reactive 核心包中拆出来,从而避免自包含应用在根本用不到这些框架时,还被强行带入几十 MB 的依赖。
Reactive Extensions for .NET 提供了一套基于 IObservable<T> 的编程模型,以及用于组合异步和事件驱动数据流的各类操作符。自从 2023 年 1 月英国公司 endjin 接过 Rx.NET 的维护工作以来,各个版本的重心都放在现代化改造和清理历史技术债务上,而不是继续堆操作符。Rx.NET 7.0 同样延续了这一思路,核心功能变化基本就这一项。
这个问题主要出现在同时采用自包含部署或 Native AOT,以及带上 Windows 特定 TFM(Target Framework Moniker,即目标框架名字)的应用里,比如 net8.0-windows10.0.19041。只要引用了 System.Reactive,哪怕应用完全没用到 WPF 或 Windows Forms,完整的框架也可能被塞进最终的部署产物。按照 endjin 的说法:不启用裁剪时,这会额外增加约 90 MB;启用裁剪后仍会增加约 47 MB;改用 Native AOT 构建,额外体积则约为 11 MB。
Rx.NET 7.0 的解决思路是把 UI 相关功能拆到独立的 NuGet 包里,分别是 System.Reactive.Windows.Forms、System.Reactive.Wpf、System.Reactive.WindowsRuntime 和 System.Reactive.Uwp。升级之后,如果你的应用确实用到了这些集成能力,就需要显式引用对应的 NuGet 包。而那些压根没用过这些功能的项目,源代码不用改,受影响的自包含部署就能明显瘦下来。
维护团队也在努力保证拆包后的二进制兼容性。旧版 UI API 仍然保留在 NuGet 包提供的运行时程序集(runtime assemblies)里,只是不再出现在引用程序集(reference assemblies)中。这意味着,用 Rx.NET 6.1 编译好的组件理论上还能继续运行;而重新编译的应用如果没有添加对应包,就无法再悄悄依赖 Windows UI 功能。另外,System.Reactive 7.0 还加了一个 Analyzer,可以检测受影响的源代码,并提示需要补哪个包。
当然,这个版本也包含一些破坏性变更。Rx.NET 7.0 不再支持 .NET 6 和 .NET 7,但仍继续支持 .NET 8、.NET 9、.NET 10、.NET Framework 4.7.2、.NET Standard 2.0 以及 UWP。此外,它还修正了 OfType 的可空性标注(nullability annotations)。运行时行为本身没变,但方法签名有变化,按照语义化版本(Semantic Versioning)的规则,这在技术上也需要提升主版本号。
对于仍在用旧式 packages.config 的项目,还有一个特殊情况需要留意。这种格式无法区分 NuGet 的 ref 和 lib 资产,导致这些项目即使没有显式引用新的 UI 包,也仍然可能看到这些 UI API。Rx.NET 维护团队目前并不支持这种配置,而且表示未来可能会移除为了兼容这一机制而保留的代码。
此前 Rx.NET 用户的讨论里已经有人提到 Windows 特定的打包问题,以及项目能不能跟上现代 .NET 部署模型的问题。有开发者反馈,在某些目标框架组合下,他们得自己想办法绕过缺失的 Windows Scheduler API;也有人希望 Rx.NET 能在裁剪、AOT 支持和包文档方面做得更好。目前 7.0 发布后的公开讨论还比较有限,因此现在就判断社区对这个版本的整体反响,显然还为时过早。
随着 7.0 发布,endjin 表示,他们接手 Rx.NET 维护工作时制定的路线图主要目标已经完成。接下来的工作可能会转向进一步减少内存分配、引入代码生成、探索类似 ref 元素的支持,以及继续增加操作符。目前 System.Reactive 7.0 已经可以通过 NuGet 获取;关于这次拆包设计和后续开发计划的更多细节,可以去 Rx.NET GitHub 仓库里查看。
查看英文原文:Rx.NET 7.0 Reduces Deployment Size by Splitting Windows UI Support (https://www.infoq.com/news/2026/08/rx-net-7/)
|