前端移动开发桌面应用跨平台异步编程【免费下载链接】ReactiveUIA composable, cross-platform MVVM framework for .NET inspired by functional reactive programming. It moves mutable state out of the user interface, keeps each features logic in one readable place, and makes applications easier to test. Supports WPF, WinForms, WinUI, .NET MAUI, Avalonia, Uno Platform, Blazor and Android.项目地址https://gitcode.com/gh_mirrors/re/ReactiveUI点击查看免费下载导读CONTRIBUTING_PERFORMANCE.md是 ReactiveUI 仓库内部的热路径hot-path工程规则手册它定义了src/ReactiveUI/及平台扩展库中生产代码必须遵守的分配allocation、异步async、类型设计与 API 形态标准。本文以该文档为骨架结合仓库内的调度器实现、可回收内部原语与 BenchmarkDotNet 基准套件源码逐条拆解这些规范背后的原理与落地方式帮助你理解 ReactiveUI 如何在不破坏公共契约的前提下压低每次订阅、每次属性发射的开销并为向该仓库提交高质量代码提供一份可执行的检查清单。1. 文档定位与双层规范体系CONTRIBUTING_PERFORMANCE.md是 agent.md 与 CONTRIBUTING.md 的性能向配套文档。仓库对文档的职责划分如下agent.md优先构建/测试命令、仓库布局、SLNX/MTP/TUnit 工具链、AOT 指引性能文档不重复 AOT 章节性能文档优先热路径细节 —— 分配纪律、System.Reactive 边界、异步规则、类型设计与性能驱动的 API 形态规则官网贡献指南负责叙事性上手引导。文档开头明确了一个总体策略性能标准正在收紧。新代码必须逐条满足全部规则存量代码则采用“机会式迁移”——当你触碰某个文件时把改动到的行提升到标准水平。1.1 内部代码严格模式不属于已发布 API 的一切 ——private/internal成员、sink 与 operator 实现、辅助类、表达式重写机制 ——必须完整应用每一条规则。关键原则是内部契约并不神圣。如果一项性能驱动的改动会破坏内部契约那就改掉契约本身通过[InternalsVisibleTo]消费内部类型的测试与兄弟平台程序集跟着一起迁移即可。1.2 公共 API谨慎模式消费者可以编译依赖的一切 —— 公共类型、public/protected 成员、公共返回与参数类型、已发布的默认参数值 ——存量签名只能通过规范的弃用与版本化流程变更[Obsolete]→ 跨大版本移除绝不能静默修改。当规则与已发布的公共签名冲突时该规则应被视作新表面的指导与旧表面的迁移目标。从源码结构看这一双层设计直接对应仓库的工程布局src/ReactiveUI.Shared/、src/ReactiveUI.Core/承载跨平台核心逻辑src/ReactiveUI/Platforms/承载各平台实现公共 API 基线则由各项目的PublicAPI/目录如 PublicAPI 清单跟踪。2. System.Reactive 边界契约保留、实现自有化ReactiveUI 构建在 Rx 契约之上这一点不会改变。改变的是 operator 的实现者项目正从“热路径走System.Reactive.Linq.Observable”转向“自研低分配 operator / observer sink”。2.1 永远保留 BCL/Rx 契约类型IObservableT、IObserverT、IScheduler和Unit是每个消费者依赖的生态互操作契约必须直接依赖并透传绝不平行重造IObservableT/IObserverT位于 BCL 的System命名空间不属于System.Reactive无论如何都会保留IScheduler与Unit是仅有的两个被保留的System.Reactive类型。这是“自己造轮子”原则的刻意例外——平行实现替代品只会割裂契约而毫无收益。2.2 内部可回收原语自有 Disposables 族项目不依赖System.Reactive.Disposables改用自有内部原语命名避开System.Reactive类型以避免消费者作用域内命名冲突。文档给出的映射表如下自有原语语义替代的 System.Reactive 类型DisposableBag子 disposables 的复合体CompositeDisposableMutableDisposable单槽可重赋值交换时不释放旧值MultipleAssignmentDisposableSwapDisposable单槽可重赋值交换时释放旧值SerialDisposableOnceDisposable只写一次槽位SingleAssignmentDisposableEmptyDisposable共享无操作单例Disposable.EmptyActionDisposable在 dispose 时执行 actionDisposable.Create(Action)这些原语是定制化、低分配的线程意识仅做到调用点所需程度并非System.Reactive形态的 1:1 换名。仓库证据ReactiveShim.props 的注释记录了 disposables 迁移的来龙去脉仓库原本 fork 了同名类型后来迁到ReactiveUI.Primitives.DisposablesMultipleDisposable、Scope取代了最后残存的System.Reactive.Disposables类型SerialDisposable/SingleAssignmentDisposable调用点迁移到SwapDisposable/MutableDisposable。包版本由中央包管理统一约束见 src/Directory.Packages.props 中的ReactiveUI.Disposables。源码中的实际用法示例OnceDisposable用于视图激活状态机CanActivateViewFetcher.csSwapDisposable用于 View/ViewModel 宿主交换ViewForMixins.csDisposableBag聚合订阅如 InteractionBinderImplementation.cs、PropertyBindingExpressionCompiler.cs。2.3CompositeDisposable只留在公共 API 边缘凡 ReactiveUI已发布的公共表面暴露了CompositeDisposable消费者可编译依赖的参数或返回类型就保持原样——该签名即契约改动会破坏消费者。内部代码与所有新增非公共代码一律使用DisposableBag绝不允许在内部代码路径引入System.Reactive.Disposables类型。2.4 热路径优先自研 operator / observer sink在 binding、WhenAny*或命令管道逐值发射之处优先使用按需定制的 sink可sealed、可避免每个 operator 的闭包开销、可融合相邻阶段而非链式拼接System.Reactive.Linq的Select、Where、CombineLatest、Merge、Throttle等 operator。新增对System.Reactive.Linq的依赖前必须先审计如果热路径“需要”Observable.Foo首选答案通常是自研 sink。冷路径一次性初始化、配置、示例文档代码与刻意返回可组合IObservableT的公共 API 边缘则不受此限。基准源码印证了这一方向WhenAnyValueBenchmarks.cs 直接测量WhenAnyValuesink 的冷订阅成本与逐变更的值传播且选择器已经使用staticlambda见下文第 3 节。2.5 调度抽象RxSchedulers优先于RxApp调度抽象使用RxSchedulersAOT 友好按 agent.md 指引热路径绝不内联写死调度器默认值——应该接受一个传入的调度器。源码证据RxSchedulers.cs 提供MainThreadScheduler默认Sequencer.Default即 UI 线程与TaskpoolScheduler默认TaskPoolSequencer.Default后台线程池其 XML 注释明确指出“无需 unreferenced code attributes适合 AOT 编译场景”。在 ReactiveCommand.cs 中outputScheduler的默认值均标注为RxSchedulers.MainThreadScheduler执行侧则回落到RxSchedulers.TaskpoolScheduler正是“公共签名允许默认值、热路径必须可注入”的落地示例。3. 分配纪律性能标准的核心以下规则适用于生产代码测试项目放宽分配规则见第 12 节。3.1 生产代码零 LINQ生产热路径禁用System.Linq。LINQ 每次调用都会引入 lambda、迭代器与装箱的枚举器。改用普通索引for循环。此规则针对IEnumerableT上的System.LinqIObservableT上的 Rx operator 由第 2 节 System.Reactive 边界管辖。3.2for优先于foreach对数组 /SpanT/ReadOnlySpanT/ListT/IReadOnlyListT使用索引for。仅当类型确实没有索引器HashSetT、IAsyncEnumerableT、字典枚举时才用foreach。3.3 无捕获时一律用staticlambdastatic x …/static (state, x) …让 JIT 跳过闭包分配。这在WhenAnyValue/ binding 选择器每次订阅都分配中最重要——把捕获状态通过元组/state 参数传递而不是闭包局部变量。基准源码正是这么写的WhenAnyValueBenchmarks.cs中_arity2ViewModel.WhenAnyValue(x x.First, x x.Second, static (first, second) (first, second))三参数版本同理WhenAnyValueBenchmarks.cs。3.4 集合分配纪律数组优先于ListT最终长度已知就预分配、按下标写入ListT不可避免时必须传 capacitynew ListT(expectedCount)绝不无 capacity 创建预置Dictionary/HashSet容量容量提示应反映预期规模热路径避免ImmutableArrayT/ImmutableListT包装结构每次读取多一次间接寻址builder 会翻腾中间数组。仅当 API 确实公开且消费者不可变更时才使用不可变集合否则暴露IReadOnlyListT/T[]并按约定视为不可变集合表达式[..]优先[a, b, ..tail]、[]、[..source]用于最终物化能用集合表达式就不要.ToArray()缓冲池化在逐发射分配的管道中用ArrayPoolT.Shared.Renttry/finally Return池化瞬态缓冲区。3.5 锁与计数器简单计数器争用场景用Interlocked.Increment/Interlocked.Decrementlock只留给真正的多字段不变量System.Threading.Locknet9是新代码的默认 monitor 原语private readonly Lock _gate new();配lock (_gate)net8.0/net462/net472/net481回退到private readonly object _gate new();多 TFM 差异可用辅助类隐藏绝不在任意对象上加锁this、typeof(X)、公共字段永远使用专用_gate字段。源码印证RxSchedulers.cs 正是文档的活体示例——net9 用private static readonly Lock _lock new();旧 TFM 回退到object并且调度器字段用volatile修饰保证无锁读取getter 不加锁与加锁写入。4. 字符串保留一等公民地位但不浪费分配ReactiveUI 本质上是“字符串形态”的属性名驱动RaiseAndSetIfChanged/nameof绑定路径与表达式链解析到成员名公共 API 自由交换string。项目不采用无字符串 / UTF-8 字节策略string仍是公共与内部的一等类型。仍然适用的规则热路径不无谓分配字符串逐发射路径内禁止用插值或string.Format构建随后被丢弃、或仅在失败分支使用的值——消息要惰性构建放在 throw 路径上成员引用用nameof(...)而非字符串字面量agent.md 已要求标识符、类型名、属性名比较及以其为键的字典/集合一律用StringComparer.Ordinal/StringComparison.Ordinal——文化感知比较在此既错误又慢数倍Span 做廉价解析/切片拆分成员路径时优先ReadOnlySpanchar 区间表达式path[..i]、name[^1]避免Substring的临时分配。5. 异步规则端到端异步管道ReactiveUI 的异步面——ReactiveCommand.CreateFromTask、interaction 处理器、异步绑定——管道必须端到端异步。禁止 sync-over-async异步路径内绝不.GetAwaiter().GetResult()、.Result、.Wait()。契约给了CancellationToken就await生产代码每个库await都配ConfigureAwait(false)测试不需要取消必须流通异步 operator 在契约支持处接受CancellationToken并向下传递——绝不吞掉绝不把作用域内的真实 token 换成CancellationToken.None。CancellationTokenSource.CreateLinkedTokenSource在订阅时创建一次而非每次发射都建ValueTask优先当零分配已证实否则Task大多数实现同步完成且调用点倍增逐发射路径时用ValueTask真正异步主导I/O或冷路径每次设置调用一次用Task。遵守ValueTask的一次性消费规则绝不重复await同一实例绝不存入字段同步实现返回缓存的已完成任务 ValueTask.CompletedTask/Task.CompletedTask无状态机、零分配。6. 模式匹配与流程控制反转if展平快乐路径守卫子句 提前return/continue在前主逻辑保持不缩进守卫分支上不加elseswitch 表达式优先于if/else链属性模式{ HasValue: true }、位置模式、列表模式。优先级switch 表达式 → switch 语句 →if/else if链仅当前者无法表达分发ref/out修改、副作用、fall-through时才降级列表模式表达空/基数is [_, ..]优于.Count 0is []表空is [var single]绑定单元素集合is/is not优先于/!做空值与类型检查一行内组合类型测试与属性检查避免while (true)终止条件写在循环头。唯一例外是真正无界、靠 token 退出的泵应写作while (!cancellationToken.IsCancellationRequested)。7. API 形态可演进、可内联、可物化新 API 不加默认参数值默认值会把常量烘焙进每个调用者的 IL后续调整需要所有消费者重新编译。应提供显式重载各自委托给参数最全的那个重载。双层说明ReactiveUI 已发布公共 APIWhenAnyValue、ToProperty、调度器参数已有默认值保持原样、不破坏消费者此规则适用于新公共表面与全部内部表面。新生产 API 尽量用具体集合类型参数与返回值用IReadOnlyListT/T[]/DictionaryK,V/HashSetT而非IEnumerableT仅当流式 yield 确实避免物化序列时才用IEnumerableT。已发布的返回接口类型的公共签名保持不变加包锁最新非 beta 版本在 src/Directory.Packages.props 中新增依赖时通过 NuGet flatcontainer 版本查询接口形如https://api.nuget.org/v3-flatcontainer/小写包id/index.json确认最高稳定版禁止-preview/-rc/-alpha/-beta升级同样适用。仓库当前的中央包管理启用了ManagePackageVersionsCentrally与CentralPackageTransitivePinningEnabledsrc/Directory.Packages.props版本集中在同文件的PackageVersion项中管理——这正是“钉版本”规则的工程载体。8. 类型设计默认sealed每个类不为继承设计的类型一律sealed利于内联并去除意外 override 面。公共基类型ReactiveObject、视图基类按设计保持开放readonly record struct用于不可变值形态数据字段少≤ 4–5 个或仅持引用免费获得相等性/哈希无 GC 压力sealed recordclass用于参与继承层次或字段较多的记录方法尽量static不触碰this的方法应为static——更少隐藏分配、调用点更清晰、免费去虚拟化类中若全是静态方法类本身也标staticinternal static辅助类承载无状态跨类型工具按职责分组公共表面保持窄单例比较器private sealed class XComparer : IComparerTpublic static readonly XComparer Instance避免每次Array.Sort/ 字典操作分配新比较器或 lambda长参数列表捆绑为readonly record struct或ref struct状态类型记录值间关系并让 JIT 把值保持在寄存器中。9. 属性field关键字与响应式 setter 的分工属性默认使用 C#field关键字需要后备逻辑懒初始化、校验、变更追踪时在访问器内用field而非独立_name字段。仅在以下情形保留显式后备字段ref传递 APIInterlocked.Increment(ref _counter)、Volatile.Read(ref _state)、Unsafe.AsT(ref _slot)需一行注释说明构造函数赋值须绕过 setter 逻辑属性外的方法引用存储罕见RaiseAndSetIfChanged仍是响应式 setter 的规范写法field指引针对非响应式后备逻辑响应式属性继续用this.RaiseAndSetIfChanged(ref field, value) 显式后备字段——它正是上述第一个例外ref传递 API。10. 异常辅助、Span 与只读高频查找异常辅助自组装消息优先ArgumentNullException.ThrowIfNull(x)与基于[CallerArgumentExpression]的辅助方法而不是手写if (x is null) throw …调用点只传值、不传nameof(x)。仓库中对应实现可参考 Polyfills 目录 与 ArgumentValidation.cs重复多字符搜索用SearchValuesT缓存为private static readonly SearchValueschar传给IndexOfAny/IndexOfAnyExcept——任何命中超过一次的场景都快于IndexOfAny([...])写 span 缓冲用TryFormat/TryParse而非ToString/ParseFrozenDictionaryK,V/FrozenSetT仅当四条件全满足构建一次 → 查询多次 → 构造后只读 → 真正高频或广泛共享。冻结过程本身昂贵每实例、短生命周期或每订阅的表不要用Frozen*——带正确比较器的普通Dictionary/HashSet更优。11. 分析器与抑制策略ReactiveUI 运行 StyleCop、Roslynator、.NET CA 分析器并新增了SonarAnalyzer.CSharp与Blazor.Common.Analyzers。这些工具能捕获真实的性能与正确性问题。修复代码而非压制规则几乎每个分析器命中都有结构性修复方案——抽取辅助方法、反转守卫、改变返回类型、重构 throw——优于抑制零 pragma 政策见 agent.md生产代码无#pragma warning disable。StyleCopSA****警告必须修复、绝不抑制[SuppressMessage]是最后手段且必须说明理由使用逐符号[SuppressMessage(Category, RuleId, Justification …)]指明具体原因。CA 规则抑制仅在修复确实伤害设计时才允许同一规则第二次命中通常意味着设计本身有问题——应修复设计生产项目零NoWarn政策.csproj/.props/.targets中的项目级NoWarn需要明确协商且基本不会被批准。12. 测试与基准标准的分层应用12.1 测试TUnit Microsoft Testing Platformsrc/tests/下使用TUnit Microsoft Testing Platform构建/测试命令见 agent.md。测试即文档——命名与断言传达契约集成测试优先真实实现而非 mock。测试的分配规则放宽foreach、System.Linq、无 capacity 的ListT在测试中允许可读性优先于微优化但风格、模式匹配与抑制规则仍然适用。12.2 基准BenchmarkDotNet [MemoryDiagnoser]热路径工作使用src/benchmarks/ReactiveUI.Benchmarks/下的 BenchmarkDotNet 基准必须加[MemoryDiagnoser]吞吐与分配一起跟踪新增或修改热路径特性时配套新增基准在perf提交正文中引用其数字。仓库证据Program.cs 用BenchmarkSwitcher.FromAssembly(...).Run(args)接收命令行过滤参数WhenAnyValueBenchmarks.cs 展示了一套完整的基准骨架[MemoryDiagnoser][MarkdownExporterAttribute.GitHub]、[GlobalSetup]建立各 arity 的独立 ViewModel 与活跃订阅、[GlobalCleanup]释放、Subscribe_*测冷订阅释放成本、Emit_*用固定发射次数如EmissionCount 10_000测逐值传播下游用NoopObserverT吸收发射以免污染测量。这套结构与文档第 8 节“单例/静态 sink”互相印证。13. 提交风格可机器扫描的 Conventional Commits仓库遵循 Conventional Commits 1.0.0使git log可机械扫描、发布说明工具可按意图分组。type(optional scope): subject body footers类型feat、fix、perf、refactor、docs、test、build、ci、chore、revert标准语义Scope受影响子系统小写、不带ReactiveUI.前缀。示例binding、whenany、command、activation、routing、interactions、builder、scheduler以及平台 scopewpf、winui、maui、androidx、blazor、winforms。改动均匀跨越多个领域时可省略 scopeSubject约 70 字符祈使语气小写开头无结尾句点Body解释为什么。perf提交必须附带基准数字before/after、场景、分配增量使成果可验证Footers任何公共 API 变更写BREAKING CHANGE: text或类型后加!如feat(binding)!:引用 issueCloses #123、Refs #456。文档给出的完整示例perf(whenany): cut per-subscription alloc by hoisting the selector to a static lambda Replace the captured-closure selector in the two-property WhenAnyValue overload with a static lambda that threads the source through a value tuple. The closure object is gone from the subscription path. Verified on WhenAnyValueBenchmarks: 92 ns - 61 ns, alloc 64 B - 0 B. Refs #123414. 落地自查清单把文档压缩为提交代码前的 10 条快速检查内部代码逐条达标公共签名变更只走弃用/版本化路径热路径不链System.Reactive.Linq不自造IObservable/IObserver/IScheduler/Unit替代品内部代码用DisposableBag/SwapDisposable/OnceDisposable等自有原语不引入System.Reactive.Disposables生产代码零System.Linqfor优先数组预分配List/Dictionary必带 capacity无捕获 lambda 一律static选择器用元组透传状态每个库await带ConfigureAwait(false)取消 token 向下流通ValueTask一次性消费新 API 无默认参数值、用具体集合类型、sealed默认、field关键字优先字符串比较用 Ordinal热路径不插值拼消息切片用 Span分析器命中先修复再谈抑制零 pragma、零NoWarn热路径改动配套[MemoryDiagnoser]基准perf提交正文给出 before/after 数字。结语CONTRIBUTING_PERFORMANCE.md本质上回答了 ReactiveUI 的一个核心工程问题在“消费者依赖的公共契约不可破坏”的前提下如何把订阅、发射、绑定这些高频路径压到极低的分配水平。答案不是推翻 Rx 契约而是保留契约类型、把热路径实现收归自研 sink、用sealed/static/field/Span 等现代 C# 特性让 JIT 有更多优化空间并用基准与提交规范让每一次性能改进可验证、可追溯。对于想要为 ReactiveUI 提交代码的开发者这份文档配合 agent.md就是最直接的准入门槛对于任何 .NET 框架作者它的分配纪律与双层 API 策略也是极具参考价值的工程样板。赞分享前端移动开发桌面应用跨平台异步编程【免费下载链接】ReactiveUIA composable, cross-platform MVVM framework for .NET inspired by functional reactive programming. It moves mutable state out of the user interface, keeps each features logic in one readable place, and makes applications easier to test. Supports WPF, WinForms, WinUI, .NET MAUI, Avalonia, Uno Platform, Blazor and Android.项目地址https://gitcode.com/gh_mirrors/re/ReactiveUI点击查看免费下载相关推荐CppCoreGuidelines 贡献指南三种贡献路径、PR 规范与 Markdown 文档校验工具链CppCoreGuidelines 贡献指南三种贡献路径、PR 规范与 Markdown 文档校验工具链 本文基于 C Core Guidelines 仓文档教程PowerToys AI 贡献者指南构建纪律、测试规范与工程边界的完整实践PowerToys AI 贡献者指南构建纪律、测试规范与工程边界的完整实践 本文以 PowerToys 仓库根目录的 AGENTS.md https://li桌面应用开发工具Pyxel 编码规范全解析确定性、热路径性能与跨文件一致性的工程实践Pyxel 编码规范全解析确定性、热路径性能与跨文件一致性的工程实践 导读 本文是对 PyxelA retro game engine for Python游戏开发上一篇终极AWS安全事件响应手册my-arsenal-of-aws-security-tools工具应急使用指南下一篇Wazuh Remoted 模块Agent 通信网关、状态无关元数据富化与 x-wev1 事件协议详解创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考