架构分析.md 6.7 KB

Shared 复用指南

代码约定 · Wpf · Tests

按功能定位

场景 源码入口
初始化、应用目录 Main.cs、PathUtils.cs
强类型键、JSON 配置 Config.cs
日志接入 Logger.cs
分阶段进度 Progress.cs
节流、防抖、取消后重跑 Worker
异步并发、进程执行 TaskUtils.cs
并发列表、联合值、范围 ConcurrentList.cs、OneOf.cs、ValueRange.cs
集合、字符串、异常、Task、HTTP 克隆扩展 Extensions
文件、目录、路径、压缩、校验 FileUtils.cs、DirectoryUtils.cs、PathUtils.cs
格式化、数学、枚举、类型、加密 Utils
注册表、Windows 增量补丁 RegistryUtils.cs、MsDelta.cs

扩展方法主要位于 MeloongCore.Extensions,MathUtils、EnumUtils 的扩展位于 MeloongCore;项目全局 using 不传播到引用方。新框架同名方法可能改变重载绑定,替换前核对语义。注册表和 MsDelta 是 Windows 专属,Shared 的目标框架不代表所有入口均可跨平台使用。

以下保留不易从名称判断的行为;具体重载和实现以链接源码为准。

初始化、配置与日志

  • 首次访问默认配置前调用 Main.Init(appName);它设置应用名和默认正则超时,不初始化 WPF 或文件日志。默认 provider 静态初始化后,修改应用名不会迁移其路径。
  • ConfigEntry<T> 绑定固定 provider,DynamicConfigEntry<T> 每次操作传 provider。替换 ConfigProviders.Default 不改变已有 entry 的绑定。
  • ConfigUtils.GetSecret 独立读取 Paths.AppData/Meloong/Dev/secret.json,使用 CryptographyUtils.AesDecrypt ,首次读取后缓存 JSON 对象;不使用配置 provider、不参与 SaveAll。文件不存在或键为 null/缺失时抛出缺键异常;解密或解析失败抛出脱敏异常,不覆盖文件。默认密钥仅用于避免明文存储,不提供可靠保密保护。
  • 同一 provider 的同一键固定使用同一类型;显式 JSON null 算已存值。Get 返回对象缓存,修改对象后需 Edit/Set 才安排保存;Edit 不是原子更新。
  • Set 即使值相同也触发 Changed;动态 entry 的 Changed 不携带 provider。PreviewGet 仅修正本次读取,不自动写回。
  • JSON 写入防抖 500 ms;明确落盘点可用 ConfigUtils.SaveAll()。DiscardCache 不保存变更;Save 写盘失败不保证恢复 dirty。整文件加载失败回退空对象,不代表所有键读取错误都会回退默认值。
  • Logger.Instance 可替换;基类不实现 LogBehavior 所表达的 UI 动作。BaseLogger 构造时启动后台线程并调用虚方法 Init;Output 通常在日志线程,HandleBehavior 在调用线程。
  • FileLogger 创建后修改路径字段可能晚于初始化;退出时先 Flush 再 Dispose,但尚未就绪时 Flush 会返回,Dispose 不保证排空或等待线程。

进度与后台任务

  • ProgressProvider 用 SplitBy/SplitTo 分阶段;Reset 不清除单调观测历史,全新任务需要新 provider。Finish/Skip 可结算子项,IsFinished 不代表业务成功。
  • ProgressObserver 订阅并节流 GetIncreasing,构造时不立即刷新,结束时 Dispose;回调不保证 UI 线程。
  • RateLimitedWorker 的 ImmediateThenMerge 为节流,ImmediateThenIgnore 忽略密集后续请求,WaitUnillNoNewInvoke 为尾随防抖。复用同一实例;Dispose 不补执行最后一次。
  • RedoableWorker 在运行中 Start 会请求取消旧负载并合并重跑,负载必须协作取消。WaitIfRunning* 只等待空闲,不重抛负载错误,也不保证事件回调已结束。
  • RedoableWorker 的 HasSucceeded/LastResult 可保留历史成功,判断最近运行用 LastSucceeded;事件在工作线程。Worker 接受同步委托,不等待 async lambda 内的异步工作。
  • TaskUtils.WhenAll 聚合进度但不限并发;ForEach 是同步并行,异步限并发用 ForEachAsync。后者失败会停止领取新项,但内部取消令牌不传入 body,在途动作需自行响应外部令牌。
  • RunProgramAsync 返回 Output/ExitCode,非零退出码不自动抛错;输出按 stdout + stderr 拼接。取消/超时不保证终止子进程树;参数会写日志,不应包含密钥。

数据结构与扩展语义

  • ConcurrentList 单次操作加锁,复合操作需共用 SyncRoot;枚举及与 List 的转换都复制列表,元素仍共享引用。
  • OneOf 的 default 没有有效分支;Is 按声明类型精确匹配,取错分支会抛错。
  • ValueRange.Intersect 无交集仍返回范围,应检查 IsEmpty;IsEmpty 不识别离散域空区间。范围可变,不应修改已作为字典键的实例。
  • EnumerableExtensions 的并行 DistinctBy 不保证首项,重复枚举沿用已见键;MaxBy/MinBy 空输入返回 default;二维数组 ToDictionary 重复键以后者覆盖。
  • RemoveIf/KeepIf 原地修改;RemoveAll 对每项仅移除一次;Except(单值) 还会去重;ForAll 立即顺序枚举且不等待 async lambda。
  • 字符串 Before/After 系列未找到分隔符时保留原文;Between 从最后一个起始分隔符之后截取,不做括号配对。Split 去空模式对空串返回空数组,SplitLines 不修剪行内空白。
  • RegexSearch 的惰性枚举仍可能抛超时;RegexSeek/RegexCheck 捕获失败,而 RegexReplace 直接抛错。
  • ReadString 不关闭输入流;默认 GetString 尝试 BOM、严格 UTF-8,再回退 GB18030。DeserializeJson 用于 JToken 子类,解析错误可能包含原文。
  • ExceptionExtensions.IsCanceled 对混有其他错误的聚合也可返回 true;RootException 只沿单条 InnerException,Flatten 扩展与 AggregateException 同名实例方法含义不同。ThrowIfCanceled 不保证保留原堆栈;IsBadNetwork 可能把主动取消判为网络不佳。
  • TaskExtensions.Run 同步阻塞;NoCapture 仅为 ConfigureAwait(false),不保证切到后台线程。
  • HTTP CloneAsync 缓冲复制全部内容,Properties 中的对象仍共享;无取消令牌重载。WebUtils.cs 定义 HttpRequestCodeException,未提供自动发送或失败状态检查。