Shared 复用指南
代码约定 · Wpf · Tests
按功能定位
扩展方法主要位于 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,未提供自动发送或失败状态检查。