Apq.ChangeBubbling
1.0.2
dotnet add package Apq.ChangeBubbling --version 1.0.2
NuGet\Install-Package Apq.ChangeBubbling -Version 1.0.2
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Apq.ChangeBubbling" Version="1.0.2" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Apq.ChangeBubbling" Version="1.0.2" />
<PackageReference Include="Apq.ChangeBubbling" />
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Apq.ChangeBubbling --version 1.0.2
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: Apq.ChangeBubbling, 1.0.2"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Apq.ChangeBubbling@1.0.2
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Apq.ChangeBubbling&version=1.0.2
#tool nuget:?package=Apq.ChangeBubbling&version=1.0.2
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
Apq.ChangeBubbling
变更冒泡事件库,提供树形数据结构的变更事件自动冒泡、Rx 响应式流、弱引用消息和可插拔调度环境。
仓库地址:https://gitee.com/apq/Apq.ChangeBubbling
📖 在线文档:https://apq-changebubbling.vercel.app/
项目结构
Apq.ChangeBubbling/
├── Abstractions/ # 抽象定义
│ ├── BubblingChange.cs # 变更事件上下文
│ ├── IBubblingChangeNotifier.cs
│ └── NodeChangeKind.cs # 变更类型枚举
├── Core/ # 核心实现
│ ├── IChangeNode.cs # 节点接口
│ ├── ChangeNodeBase.cs # 节点基类
│ └── WeakEventSubscription.cs # 弱事件订阅
├── Nodes/ # 节点实现
│ ├── ListBubblingNode.cs # 列表节点
│ ├── DictionaryBubblingNode.cs# 字典节点
│ └── Concurrent/ # 线程安全节点
├── Messaging/ # 消息中心
│ ├── ChangeMessenger.cs # 消息发布中心
│ └── BubblingChangeMessage.cs # 消息包装
├── Collections/ # 集合适配器
├── Infrastructure/ # 基础设施
│ ├── Dataflow/ # TPL Dataflow 管线
│ ├── EventFiltering/ # 事件过滤器
│ ├── Performance/ # 性能优化
│ └── Nito/ # Nito.AsyncEx 集成
└── Snapshot/ # 快照服务
特性
- 变更事件冒泡:子节点的变更事件自动向上冒泡到父节点,携带完整路径信息
- 多种消息通道:支持 Rx Subject 响应式流和 WeakReferenceMessenger 弱引用消息
- 可插拔调度环境:支持线程池、UI 线程、专用线程、Nito.AsyncEx 等多种调度模式
- 事件过滤:内置属性过滤、路径过滤、频率节流等多种过滤器
- 背压管线:基于 TPL Dataflow 的背压处理管线
- 批量操作:支持批量变更和事件合并,减少高频场景下的事件风暴
- 快照服务:支持节点树的快照导出与导入
- 线程安全:提供线程安全的并发集合节点
- 高性能:使用弱事件订阅、路径缓存、ArrayPool 等优化技术
支持的框架
- .NET 8.0
- .NET 10.0
安装
dotnet add package Apq.ChangeBubbling
快速开始
基本用法
using Apq.ChangeBubbling.Nodes;
// 创建节点树
var root = new ListBubblingNode<string>("Root");
var child = new ListBubblingNode<int>("Child");
// 建立父子关系
root.AttachChild(child);
// 订阅变更事件
root.NodeChanged += (sender, change) =>
{
Console.WriteLine($"变更: {change.PropertyName}, 类型: {change.Kind}, 路径: {string.Join(".", change.PathSegments)}");
};
// 子节点的变更会自动冒泡到父节点
child.Add(42);
child.Add(100);
使用 Rx 响应式流
using Apq.ChangeBubbling.Messaging;
// 订阅原始事件流
ChangeMessenger.AsObservable()
.Subscribe(change => Console.WriteLine($"收到变更: {change.PropertyName}"));
// 订阅节流事件流
ChangeMessenger.AsThrottledObservable(TimeSpan.FromMilliseconds(100))
.Subscribe(change => Console.WriteLine($"节流后: {change.PropertyName}"));
// 订阅缓冲批量事件流
ChangeMessenger.AsBufferedObservable(TimeSpan.FromSeconds(1), 100)
.Subscribe(changes => Console.WriteLine($"批量收到 {changes.Count} 个变更"));
配置调度环境
// 使用线程池(默认)
ChangeMessenger.RegisterThreadPool("default");
// 使用 UI 线程(需在 UI 线程调用)
ChangeMessenger.RegisterDispatcher("ui");
// 使用专用线程
var disposable = ChangeMessenger.RegisterDedicatedThread("worker", "WorkerThread");
// 发布到指定环境
ChangeMessenger.Publish(change, "ui");
事件过滤
using Apq.ChangeBubbling.Infrastructure.EventFiltering;
// 基于属性的过滤器
var propertyFilter = new PropertyBasedEventFilter(
allowedProperties: new[] { "Name", "Value" },
excludedKinds: new[] { NodeChangeKind.CollectionReset }
);
// 基于路径的过滤器
var pathFilter = new PathBasedEventFilter(
allowedPaths: new[] { "Root.Settings" },
maxDepth: 3
);
// 基于频率的过滤器(节流)
var frequencyFilter = new FrequencyBasedEventFilter(
throttleInterval: TimeSpan.FromMilliseconds(100)
);
// 组合过滤器
var compositeFilter = new CompositeEventFilter(
new IChangeEventFilter[] { propertyFilter, pathFilter },
CompositeEventFilter.FilterMode.All
);
// 注册过滤器
ChangeMessenger.RegisterFilter("default", compositeFilter);
批量操作
var node = new ListBubblingNode<int>("Numbers");
// 开始批量操作
node.BeginBatch();
try
{
for (int i = 0; i < 1000; i++)
{
node.Add(i);
}
}
finally
{
// 结束批量操作,一次性触发所有事件
node.EndBatch();
}
事件合并
var node = new ListBubblingNode<int>("Numbers");
// 开始事件合并模式
node.BeginCoalesce();
try
{
// 多次修改同一属性,只保留最终值
node.Add(1);
node.Add(2);
node.Add(3);
}
finally
{
// 结束合并,触发合并后的事件
node.EndCoalesce();
}
使用背压管线
using Apq.ChangeBubbling.Infrastructure.Dataflow;
// 创建背压管线
using var pipeline = new ChangeDataflowPipeline(
handler: change => ProcessChange(change),
boundedCapacity: 10000,
maxDegreeOfParallelism: 4
);
// 发送变更到管线
pipeline.Post(change);
// 完成处理
pipeline.Complete();
快照导出与导入
using Apq.ChangeBubbling.Snapshot;
// 导出快照
var snapshot = TreeSnapshotService.Export(rootNode);
var json = SnapshotSerializer.ToJson(snapshot);
// 导入快照
var loadedSnapshot = SnapshotSerializer.FromJson(json);
var restoredNode = TreeSnapshotService.Import(loadedSnapshot);
核心类型
节点类型
| 类型 | 描述 |
|---|---|
ChangeNodeBase |
节点基类,提供父子管理与冒泡转译 |
ListBubblingNode<T> |
基于列表的冒泡节点 |
DictionaryBubblingNode<TKey, TValue> |
基于字典的冒泡节点 |
ConcurrentBagBubblingNode<T> |
线程安全的列表冒泡节点 |
ConcurrentDictionaryBubblingNode<TKey, TValue> |
线程安全的字典冒泡节点 |
变更类型
| 类型 | 描述 |
|---|---|
PropertyUpdate |
属性值更新 |
CollectionAdd |
集合添加元素 |
CollectionRemove |
集合移除元素 |
CollectionReplace |
集合替换元素 |
CollectionMove |
集合移动元素 |
CollectionReset |
集合重置 |
过滤器类型
| 类型 | 描述 |
|---|---|
PropertyBasedEventFilter |
基于属性名和变更类型的过滤器 |
PathBasedEventFilter |
基于路径和深度的过滤器 |
FrequencyBasedEventFilter |
基于频率的节流过滤器 |
CompositeEventFilter |
组合多个过滤器 |
性能优化
库内置多项性能优化:
- 弱事件订阅:避免内存泄漏,自动清理失效订阅
- 路径缓存:缓存常用路径段,减少字符串分配
- ArrayPool:长路径使用 ArrayPool 减少 GC 压力
- 快照缓存:集合快照缓存,避免重复创建
- 无锁快速路径:批量/合并模式使用 volatile 标志位实现无锁快速检查
依赖项
| 包名 | 用途 |
|---|---|
| System.Reactive | Rx 响应式编程 |
| System.Threading.Tasks.Dataflow | TPL Dataflow 背压管线 |
| CommunityToolkit.Mvvm | 弱引用消息 |
| Castle.Core | 动态代理 |
| Nito.AsyncEx | 异步上下文 |
| PropertyChanged.Fody | 自动织入 INotifyPropertyChanged |
许可证
MIT License
作者
- 邮箱:amwpfiqvy@163.com
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
net10.0
- Castle.Core (>= 5.2.1)
- CommunityToolkit.Mvvm (>= 8.4.0)
- Nito.AsyncEx (>= 5.1.2)
- System.Reactive (>= 6.1.0)
-
net8.0
- Castle.Core (>= 5.2.1)
- CommunityToolkit.Mvvm (>= 8.4.0)
- Nito.AsyncEx (>= 5.1.2)
- System.Reactive (>= 6.1.0)
- System.Threading.Tasks.Dataflow (>= 10.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.