Skip to content

feat(eventbus): 增加EventBus服务#2878

Merged
whitecat346 merged 4 commits into
PCL-Community:devfrom
whitecat346:feat/eventbus
May 23, 2026
Merged

feat(eventbus): 增加EventBus服务#2878
whitecat346 merged 4 commits into
PCL-Community:devfrom
whitecat346:feat/eventbus

Conversation

@whitecat346
Copy link
Copy Markdown
Member

@whitecat346 whitecat346 commented May 23, 2026

这个PR新增了EventBus服务,用来实现Core和UI等组件间的通讯。所有测试已通过(除Unsubscribe_Prevents_Handler_Call因为LifeCycle的限制无法初始化Context会触发Exception,但这是预期行为)

使用方法可以直接看单侧中的内容

Summary by Sourcery

引入一个由生命周期管理的 EventBus 服务,用于在应用组件之间进行类型安全、基于通道的通信。

新特性:

  • 添加 EventBusService,用于在命名通道上发布和订阅强类型事件,并支持感知生命周期的关闭以及自动处理程序清理。
  • 引入 EventDataBase,作为事件负载的通用基础记录类型。
  • 定义 IEventHandler<TEventData>,用于可释放的异步事件处理程序,并引入实验性接口 IResponsibleEventHandler<TResponse>,用于基于响应的处理。

测试:

  • 添加单元测试,覆盖基于委托的订阅、基于 IEventHandler 的订阅,并确保在取消订阅后处理程序不会被调用。
Original summary in English

Summary by Sourcery

Introduce a lifecycle-managed EventBus service for typed, channel-based communication between application components.

New Features:

  • Add EventBusService to publish and subscribe to strongly typed events on named channels with lifecycle-aware shutdown and automatic handler cleanup.
  • Introduce EventDataBase as a common base record type for event payloads.
  • Define IEventHandler for disposable, asynchronous event handlers and an experimental IResponsibleEventHandler interface for response-based handling.

Tests:

  • Add unit tests covering delegate-based subscriptions, IEventHandler-based subscriptions, and ensuring handlers are not invoked after unsubscribing.

新功能:

  • 添加 EventBusService,用于在命名通道上发布强类型事件,并通过与生命周期关联的清理机制管理订阅。
  • 引入 EventDataBase 作为事件负载的通用基础记录类型。
  • 定义 IEventHandler<TEventData> 和实验性的 IResponsibleEventHandler<TResponse> 接口,用于异步事件处理。

测试:

  • 添加单元测试,覆盖基于委托的订阅、基于 IEventHandler 的订阅,以及取消订阅后处理程序不再被调用的场景。
Original summary in English

Summary by Sourcery

引入一个由生命周期管理的 EventBus 服务,用于在应用组件之间进行类型安全、基于通道的通信。

新特性:

  • 添加 EventBusService,用于在命名通道上发布和订阅强类型事件,并支持感知生命周期的关闭以及自动处理程序清理。
  • 引入 EventDataBase,作为事件负载的通用基础记录类型。
  • 定义 IEventHandler<TEventData>,用于可释放的异步事件处理程序,并引入实验性接口 IResponsibleEventHandler<TResponse>,用于基于响应的处理。

测试:

  • 添加单元测试,覆盖基于委托的订阅、基于 IEventHandler 的订阅,并确保在取消订阅后处理程序不会被调用。
Original summary in English

Summary by Sourcery

Introduce a lifecycle-managed EventBus service for typed, channel-based communication between application components.

New Features:

  • Add EventBusService to publish and subscribe to strongly typed events on named channels with lifecycle-aware shutdown and automatic handler cleanup.
  • Introduce EventDataBase as a common base record type for event payloads.
  • Define IEventHandler for disposable, asynchronous event handlers and an experimental IResponsibleEventHandler interface for response-based handling.

Tests:

  • Add unit tests covering delegate-based subscriptions, IEventHandler-based subscriptions, and ensuring handlers are not invoked after unsubscribing.

新功能:

  • 添加 EventBusService,用于在命名通道上发布和订阅类型化事件,并支持随生命周期自动清理。
  • 引入 EventDataBase 作为事件负载的通用基础记录类型。
  • 添加 IEventHandler 接口,用于可释放(disposable)、异步的事件处理程序,以及实验性的基于响应的处理程序接口 IResponsibleEventHandler

测试:

  • 添加单元测试,覆盖基于委托的订阅和基于 IEventHandler 的订阅,并确保取消订阅后不会再触发处理程序。
Original summary in English

Summary by Sourcery

引入一个由生命周期管理的 EventBus 服务,用于在应用组件之间进行类型安全、基于通道的通信。

新特性:

  • 添加 EventBusService,用于在命名通道上发布和订阅强类型事件,并支持感知生命周期的关闭以及自动处理程序清理。
  • 引入 EventDataBase,作为事件负载的通用基础记录类型。
  • 定义 IEventHandler<TEventData>,用于可释放的异步事件处理程序,并引入实验性接口 IResponsibleEventHandler<TResponse>,用于基于响应的处理。

测试:

  • 添加单元测试,覆盖基于委托的订阅、基于 IEventHandler 的订阅,并确保在取消订阅后处理程序不会被调用。
Original summary in English

Summary by Sourcery

Introduce a lifecycle-managed EventBus service for typed, channel-based communication between application components.

New Features:

  • Add EventBusService to publish and subscribe to strongly typed events on named channels with lifecycle-aware shutdown and automatic handler cleanup.
  • Introduce EventDataBase as a common base record type for event payloads.
  • Define IEventHandler for disposable, asynchronous event handlers and an experimental IResponsibleEventHandler interface for response-based handling.

Tests:

  • Add unit tests covering delegate-based subscriptions, IEventHandler-based subscriptions, and ensuring handlers are not invoked after unsubscribing.

新功能:

  • 添加 EventBusService,用于在命名通道上发布强类型事件,并通过与生命周期关联的清理机制管理订阅。
  • 引入 EventDataBase 作为事件负载的通用基础记录类型。
  • 定义 IEventHandler<TEventData> 和实验性的 IResponsibleEventHandler<TResponse> 接口,用于异步事件处理。

测试:

  • 添加单元测试,覆盖基于委托的订阅、基于 IEventHandler 的订阅,以及取消订阅后处理程序不再被调用的场景。
Original summary in English

Summary by Sourcery

引入一个由生命周期管理的 EventBus 服务,用于在应用组件之间进行类型安全、基于通道的通信。

新特性:

  • 添加 EventBusService,用于在命名通道上发布和订阅强类型事件,并支持感知生命周期的关闭以及自动处理程序清理。
  • 引入 EventDataBase,作为事件负载的通用基础记录类型。
  • 定义 IEventHandler<TEventData>,用于可释放的异步事件处理程序,并引入实验性接口 IResponsibleEventHandler<TResponse>,用于基于响应的处理。

测试:

  • 添加单元测试,覆盖基于委托的订阅、基于 IEventHandler 的订阅,并确保在取消订阅后处理程序不会被调用。
Original summary in English

Summary by Sourcery

Introduce a lifecycle-managed EventBus service for typed, channel-based communication between application components.

New Features:

  • Add EventBusService to publish and subscribe to strongly typed events on named channels with lifecycle-aware shutdown and automatic handler cleanup.
  • Introduce EventDataBase as a common base record type for event payloads.
  • Define IEventHandler for disposable, asynchronous event handlers and an experimental IResponsibleEventHandler interface for response-based handling.

Tests:

  • Add unit tests covering delegate-based subscriptions, IEventHandler-based subscriptions, and ensuring handlers are not invoked after unsubscribing.

@whitecat346 whitecat346 self-assigned this May 23, 2026
@pcl-ce-automation pcl-ce-automation Bot added 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查 size: L PR 大小评估:大型 labels May 23, 2026
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 23, 2026

Reviewer's Guide

引入一个与生命周期感知(lifecycle-aware)的静态 EventBusService,支持基于通道(channel)的强类型发布/订阅机制,使用通用的 EventDataBase 作为负载类型,并支持可释放(disposable)的订阅;同时提供异步事件处理程序接口,以及用于验证基于委托和基于 IEventHandler 的订阅与取消订阅行为的单元测试。

EventBusService 发布与处理器调用的时序图

sequenceDiagram
    actor Publisher
    participant EventBusService
    participant HandlerObject as IEventHandler_MyEvent

    Publisher->>EventBusService: PublishAsync(channel, MyEvent)
    EventBusService->>EventBusService: _CallChannelAsync(channel, MyEvent)
    EventBusService->>EventBusService: _CallEventHandlerAsync(MyEvent, dataHandlers)
    EventBusService-->>HandlerObject: HandleEventAsync(MyEvent)
    HandlerObject-->>EventBusService: Task
    EventBusService-->>Publisher: Task
Loading

File-Level Changes

Change Details Files
Add a lifecycle-aware static EventBus implementation with typed channels and disposable subscriptions.
  • 定义静态 EventBusService,使用以通道名称和事件类型为键的并发通道注册表,存储处理程序,并可选地保存其弱拥有者引用和拥有关系标记。
  • 实现生命周期停止钩子:在停止时标记总线正在停止、清空所有通道/处理程序,并通过 Context.Error 记录清理日志。
  • 提供 AddChannel / RemoveChannel 方法用于显式通道管理,并提供 PublishAsync 方法,将事件分发到匹配的处理程序,同时在停止过程中禁止使用。
  • 实现基于 IEventHandlerSubscribe 重载,通过 WeakReference 跟踪拥有者,在释放订阅时移除处理程序,并在订阅不再引用拥有者时可选地释放拥有者。
  • 实现基于委托的 Subscribe 重载,注册包装函数,并在订阅释放时清理处理程序字典。
  • 实现内部分发辅助方法:查找其已注册事件类型可从运行时事件类型赋值的所有处理程序,修剪失效的弱引用,并并发运行处理程序,在出现异常时记录日志而不是抛出异常。
PCL.Core/App/EventBus/EventBusService.cs
Introduce common event payload type and handler interfaces for async event processing.
  • 添加 EventDataBase 记录类型,作为所有事件负载的共享基类型,包含 IdName
  • 定义 IEventHandler<TEventData>,作为受 EventDataBase 约束的、可释放的异步处理程序接口,包含单一的 HandleEventAsync 方法。
  • 添加实验性的 IResponsibleEventHandler<TResponse> 接口,用于基于 EventDataBase 负载并返回响应的异步处理程序。
PCL.Core/App/EventBus/EventDataBase.cs
PCL.Core/App/EventBus/IEventHandler.cs
PCL.Core/App/EventBus/IResponsibleEventHandler.cs
Add unit tests covering EventBus delegate and instance handlers and unsubscription semantics.
  • 创建 EventBusServiceTest 测试类,并定义从 EventDataBase 派生的 MyEvent 记录类型供测试使用。
  • 添加测试,确保基于委托的订阅能够接收到已发布事件,并通过带超时的 TaskCompletionSource 验证负载。
  • 添加测试,验证 IEventHandler 实例能够接收事件,并且处理程序可以用期望的数据完成 TaskCompletionSource
  • 添加测试,断言在释放订阅后,处理程序在后续 PublishAsync 调用中不会被触发,并通过基于超时的方式进行验证。
PCL.Core.Test/App/EventBus/EventBusServiceTest.cs

Tips and commands

Interacting with Sourcery

  • 触发新评审: 在 Pull Request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的评审评论。
  • 从评审评论生成 GitHub issue: 在某条评审评论下回复请求 Sourcery 从该评论创建 issue。你也可以直接回复 @sourcery-ai issue 来从该评论创建 issue。
  • 生成 Pull Request 标题: 在 Pull Request 标题中任意位置写上 @sourcery-ai,即可随时生成标题。你也可以在 Pull Request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 Pull Request 摘要: 在 Pull Request 正文任意位置写上 @sourcery-ai summary,即可在指定位置生成 PR 摘要。你也可以在 Pull Request 中评论 @sourcery-ai summary 来(重新)生成摘要。
  • 生成评审者指南: 在 Pull Request 中评论 @sourcery-ai guide,即可随时(重新)生成评审者指南。
  • 批量解决所有 Sourcery 评论: 在 Pull Request 中评论 @sourcery-ai resolve,即可将所有 Sourcery 评论标记为已解决。如果你已经处理完所有评论且不再希望看到它们,这会很有用。
  • 忽略所有 Sourcery 评审: 在 Pull Request 中评论 @sourcery-ai dismiss,即可忽略所有现有的 Sourcery 评审。特别适合你想从头开始新的评审时使用——别忘了再评论一次 @sourcery-ai review 来触发新评审!

Customizing Your Experience

访问你的 dashboard 来:

  • 启用或禁用评审功能,例如 Sourcery 自动生成的 Pull Request 摘要、评审者指南等。
  • 更改评审语言。
  • 添加、移除或编辑自定义评审说明。
  • 调整其他评审设置。

Getting Help

Original review guide in English

Reviewer's Guide

Introduce a lifecycle-aware, static EventBusService that supports typed, channel-based publish/subscribe using a common EventDataBase payload type and disposable subscriptions, along with interfaces for async event handlers and unit tests validating delegate-based and IEventHandler-based subscriptions and unsubscription behavior.

Sequence diagram for EventBusService publish and handler invocation

sequenceDiagram
    actor Publisher
    participant EventBusService
    participant HandlerObject as IEventHandler_MyEvent

    Publisher->>EventBusService: PublishAsync(channel, MyEvent)
    EventBusService->>EventBusService: _CallChannelAsync(channel, MyEvent)
    EventBusService->>EventBusService: _CallEventHandlerAsync(MyEvent, dataHandlers)
    EventBusService-->>HandlerObject: HandleEventAsync(MyEvent)
    HandlerObject-->>EventBusService: Task
    EventBusService-->>Publisher: Task
Loading

File-Level Changes

Change Details Files
Add a lifecycle-aware static EventBus implementation with typed channels and disposable subscriptions.
  • Define a static EventBusService with a concurrent channel registry keyed by channel name and event type, storing handlers with optional weak owner references and ownership flags.
  • Implement lifecycle stop hook to mark the bus as stopping, clear all channels/handlers, and log the cleanup via Context.Error.
  • Provide AddChannel/RemoveChannel methods for explicit channel management and a PublishAsync method that dispatches events to matching handlers while preventing use when stopping.
  • Implement Subscribe overload for IEventHandler-based subscribers that tracks owners via WeakReference, removes handlers on dispose, and optionally disposes the owner when no longer referenced by any subscription.
  • Implement Subscribe overload for delegate-based handlers that registers wrapper functions and cleans up handler dictionaries when subscriptions are disposed.
  • Implement internal dispatch helper that finds all handlers whose registered event type is assignable from the runtime event type, prunes dead weak references, and runs handlers concurrently while logging exceptions instead of throwing.
PCL.Core/App/EventBus/EventBusService.cs
Introduce common event payload type and handler interfaces for async event processing.
  • Add EventDataBase record as a shared base type for all event payloads carrying an Id and Name.
  • Define IEventHandler as an IDisposable async handler interface constrained to EventDataBase, with a single HandleEventAsync method.
  • Add experimental IResponsibleEventHandler interface for async handlers that return a response based on an EventDataBase payload.
PCL.Core/App/EventBus/EventDataBase.cs
PCL.Core/App/EventBus/IEventHandler.cs
PCL.Core/App/EventBus/IResponsibleEventHandler.cs
Add unit tests covering EventBus delegate and instance handlers and unsubscription semantics.
  • Create EventBusServiceTest test class with a MyEvent record deriving from EventDataBase to use in tests.
  • Add test ensuring delegate-based subscription receives published events and validates payload via TaskCompletionSource with timeout.
  • Add test verifying an IEventHandler instance receives events and that the handler can complete a TaskCompletionSource with expected data.
  • Add test asserting that disposing a subscription prevents the handler from being invoked on subsequent PublishAsync calls, using timeout-based verification.
PCL.Core.Test/App/EventBus/EventBusServiceTest.cs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了两个问题,并给出了一些整体性的反馈:

  • 两个 Subscribe 重载都重复了相同的通道 / 处理程序注册与反注册逻辑;可以考虑把公共代码提取到一个共享的辅助方法中,以减少重复并保持行为一致。
  • 在 _StopAsync 中,对基于 IEventHandler 的订阅去释放其 Owner 可能会让人意外,因为处理程序实例的所有权在调用方;可以考虑不要在这里释放外部处理程序,或者通过文档 / 重命名让这一生命周期责任表达得更清晰。
  • 如果 IResponsibleEventHandler 确实打算废弃,请把注释替换为 [Obsolete] 特性(或直接移除该接口),这样调用方可以在编译期得到明确的信号,而不是依赖代码注释。
面向 AI 代理的提示
请根据此次代码审查中的评论进行修改:

## 整体评论
- 两个 Subscribe<TEventData> 重载都重复了相同的通道 / 处理程序注册与反注册逻辑;可以考虑把公共代码提取到一个共享的辅助方法中,以减少重复并保持行为一致。
-_StopAsync 中,对基于 IEventHandler 的订阅去释放其 Owner 可能会让人意外,因为处理程序实例的所有权在调用方;可以考虑不要在这里释放外部处理程序,或者通过文档 / 重命名让这一生命周期责任表达得更清晰。
- 如果 IResponsibleEventHandler<TResponse> 确实打算废弃,请把注释替换为 [Obsolete] 特性(或直接移除该接口),这样调用方可以在编译期得到明确的信号,而不是依赖代码注释。

## 单独评论

### 评论 1
<location path="PCL.Core/App/EventBus/EventBusService.cs" line_range="30-32" />
<code_context>
+                {
+                    foreach (var entry in handlersByType.Values)
+                    {
+                        if (entry.Owner is IDisposable d)
+                        {
+                            try { d.Dispose(); } catch { /* ignore */ }
+                        }
+                    }
</code_context>
<issue_to_address>
**issue (bug_risk):** 如果同一个处理程序实例被多次订阅,在这里释放 `Owner` 可能会被执行多次。

由于 `Owner` 是按订阅存储的,同一个实例可以被注册多次,从而在 `_StopAsync` 中被重复释放。如果这里的某些 `IDisposable` 实现不是幂等的,就可能引入一些隐蔽的 bug。可以在关闭过程中跟踪已经释放过的 owner(例如使用带有合适相等性比较的 `HashSet<object>`),或者把释放的责任下沉到订阅 / owner 本身,这样 `_StopAsync` 就不需要直接调用 `Dispose`。
</issue_to_address>

### 评论 2
<location path="PCL.Core/App/EventBus/IResponsibleEventHandler.cs" line_range="5-6" />
<code_context>
+
+namespace PCL.Core.App.EventBus;
+
+// I think this is too hard to implement. So this is Obsolete.
+public interface IResponsibleEventHandler<TResponse>
+{
+    /// <summary>
</code_context>
<issue_to_address>
**suggestion:** 该接口在注释中被标为过时(obsolete),但没有使用特性标记,而且其参数 `EventDataBase` 类型过于宽泛。

既然打算废弃这个接口,请考虑要么直接删除它,要么用 `[Obsolete]` 标记,避免产生新的用法。如果要保留,建议为事件数据增加一个类型参数(例如 `IResponsibleEventHandler<TEventData, TResponse> where TEventData : EventDataBase`),这样事件数据和响应类型可以保持关联,并与 `IEventHandler<TEventData>` 以及事件总线 API 的其余部分保持一致。
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,请考虑分享 ✨
请帮我变得更有用!请对每条评论点 👍 或 👎,我会根据你的反馈持续改进评审质量。
Original comment in English

Hey - I've found 2 issues, and left some high level feedback:

  • Both Subscribe overloads duplicate the same channel/handler registration and unregistration logic; consider extracting the common code into a shared helper to reduce duplication and keep behavior consistent.
  • In _StopAsync, disposing the Owner for IEventHandler-based subscriptions may be surprising since the handler instance is owned by the caller; consider either not disposing external handlers here or documenting/renaming to make this lifecycle responsibility explicit.
  • If IResponsibleEventHandler is intended to be deprecated, replace the comment with an [Obsolete] attribute (or remove the interface) so callers get a clear compile-time signal instead of relying on a code comment.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Both Subscribe<TEventData> overloads duplicate the same channel/handler registration and unregistration logic; consider extracting the common code into a shared helper to reduce duplication and keep behavior consistent.
- In _StopAsync, disposing the Owner for IEventHandler-based subscriptions may be surprising since the handler instance is owned by the caller; consider either not disposing external handlers here or documenting/renaming to make this lifecycle responsibility explicit.
- If IResponsibleEventHandler<TResponse> is intended to be deprecated, replace the comment with an [Obsolete] attribute (or remove the interface) so callers get a clear compile-time signal instead of relying on a code comment.

## Individual Comments

### Comment 1
<location path="PCL.Core/App/EventBus/EventBusService.cs" line_range="30-32" />
<code_context>
+                {
+                    foreach (var entry in handlersByType.Values)
+                    {
+                        if (entry.Owner is IDisposable d)
+                        {
+                            try { d.Dispose(); } catch { /* ignore */ }
+                        }
+                    }
</code_context>
<issue_to_address>
**issue (bug_risk):** Disposing `Owner` here can run multiple times if the same handler instance is subscribed more than once.

Since `Owner` is stored per subscription, the same instance can be registered multiple times and thus disposed repeatedly in `_StopAsync`. If any `IDisposable` here is not idempotent, this can introduce subtle bugs. Either track already-disposed owners (e.g., via a `HashSet<object>` with appropriate equality) during shutdown, or move disposal responsibility into the subscription/owner so `_StopAsync` doesn’t call `Dispose` directly.
</issue_to_address>

### Comment 2
<location path="PCL.Core/App/EventBus/IResponsibleEventHandler.cs" line_range="5-6" />
<code_context>
+
+namespace PCL.Core.App.EventBus;
+
+// I think this is too hard to implement. So this is Obsolete.
+public interface IResponsibleEventHandler<TResponse>
+{
+    /// <summary>
</code_context>
<issue_to_address>
**suggestion:** The interface is commented as obsolete but not marked with an attribute and has a loosely-typed `EventDataBase` parameter.

Since this is intended to be obsolete, please either remove the interface or mark it with `[Obsolete]` to avoid new usages. If you keep it, consider adding a type parameter for the event data (e.g. `IResponsibleEventHandler<TEventData, TResponse> where TEventData : EventDataBase`) so the event data and response types stay associated and consistent with `IEventHandler<TEventData>` and the rest of the event bus API.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread PCL.Core/App/EventBus/EventBusService.cs Outdated
Comment thread PCL.Core/App/EventBus/IResponsibleEventHandler.cs
@whitecat346
Copy link
Copy Markdown
Member Author

@sourcery-ai review

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些整体性的反馈:

  • 如果 IResponsibleEventHandler<TResponse> 本身是有意不打算被使用的,建议不要只加注释说明,而是实际标记上 [Obsolete](或者直接移除),这样使用方在编译期就能得到提示。
  • _CallEventHandlerAsync 中,当发布的事件没有匹配的处理程序时会记录错误日志并抛出异常;如果这种情况在正常使用中是可预期的,你可能需要将其视为 no-op(或者至少不要抛异常),以避免在没有任何订阅者时出现无意义的失败。
给 AI 代理的提示
Please address the comments from this code review:

## Overall Comments
- If `IResponsibleEventHandler<TResponse>` is intentionally not meant for use, consider actually marking it with `[Obsolete]` (or removing it) rather than only leaving a comment, so consumers get compile-time guidance.
- In `_CallEventHandlerAsync`, publishing an event with no matching handlers logs an error and throws; if this situation is expected in normal usage, you may want to treat it as a no-op (or at least not throw) to avoid spurious failures when nothing is subscribed.

## Individual Comments

### Comment 1
<location path="PCL.Core/App/EventBus/IResponsibleEventHandler.cs" line_range="1-6" />
<code_context>
+
+namespace PCL.Core.App.EventBus;
+
+// I think this is too hard to implement. So this is Obsolete.
+public interface IResponsibleEventHandler<TResponse>
+{
+    /// <summary>
</code_context>
<issue_to_address>
**suggestion:** Consider marking this interface as [Obsolete] instead of using a comment.

The code comment marks this interface as obsolete, but the compiler can’t enforce that. Prefer adding an `[Obsolete("Use X instead")]` attribute (and planning for eventual removal) so callers are warned and new usages are discouraged.

```suggestion
using System;
using System.Threading.Tasks;

namespace PCL.Core.App.EventBus;

[Obsolete("IResponsibleEventHandler is obsolete and will be removed in a future version.")]
public interface IResponsibleEventHandler<TResponse>
```
</issue_to_address>

Sourcery 对开源项目是免费的——如果你喜欢我们的代码审查,请考虑分享一下 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的代码审查。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • If IResponsibleEventHandler<TResponse> is intentionally not meant for use, consider actually marking it with [Obsolete] (or removing it) rather than only leaving a comment, so consumers get compile-time guidance.
  • In _CallEventHandlerAsync, publishing an event with no matching handlers logs an error and throws; if this situation is expected in normal usage, you may want to treat it as a no-op (or at least not throw) to avoid spurious failures when nothing is subscribed.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- If `IResponsibleEventHandler<TResponse>` is intentionally not meant for use, consider actually marking it with `[Obsolete]` (or removing it) rather than only leaving a comment, so consumers get compile-time guidance.
- In `_CallEventHandlerAsync`, publishing an event with no matching handlers logs an error and throws; if this situation is expected in normal usage, you may want to treat it as a no-op (or at least not throw) to avoid spurious failures when nothing is subscribed.

## Individual Comments

### Comment 1
<location path="PCL.Core/App/EventBus/IResponsibleEventHandler.cs" line_range="1-6" />
<code_context>
+
+namespace PCL.Core.App.EventBus;
+
+// I think this is too hard to implement. So this is Obsolete.
+public interface IResponsibleEventHandler<TResponse>
+{
+    /// <summary>
</code_context>
<issue_to_address>
**suggestion:** Consider marking this interface as [Obsolete] instead of using a comment.

The code comment marks this interface as obsolete, but the compiler can’t enforce that. Prefer adding an `[Obsolete("Use X instead")]` attribute (and planning for eventual removal) so callers are warned and new usages are discouraged.

```suggestion
using System;
using System.Threading.Tasks;

namespace PCL.Core.App.EventBus;

[Obsolete("IResponsibleEventHandler is obsolete and will be removed in a future version.")]
public interface IResponsibleEventHandler<TResponse>
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread PCL.Core/App/EventBus/IResponsibleEventHandler.cs
@whitecat346
Copy link
Copy Markdown
Member Author

all done, wait for review

@MoYuan-CN
Copy link
Copy Markdown
Member

@sourcery-ai review

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并留下了一些整体性的反馈:

  • IResponsibleEventHandler<TResponse> 上的注释写着它已过时,但是该接口本身并没有标记为过时;建议要么添加一个带有清晰说明信息的 [Obsolete] 特性,要么在你准备好支持这种模式之前移除这条注释。
  • 如果通道不是通过 AddChannel 创建的,两个 Subscribe 重载都会抛出异常并记录错误日志;如果这不是设计上严格要求的行为,建议要么在首次使用时自动创建通道,要么返回一个更清晰、且不记录日志的失败路径,以避免产生噪声日志和难以诊断的运行时错误。
  • 当在 _CallEventHandlerAsync 中找不到任何处理程序时,该方法总是记录错误;如果对某些事件来说“没有订阅者”是正常情况,建议将该日志级别降级为 debug/trace,或者做成可配置,以避免在典型流程中产生过多的错误日志。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The comment on `IResponsibleEventHandler<TResponse>` says it is obsolete but the interface itself is not marked as such; consider adding an `[Obsolete]` attribute with a clear message or removing it until you are ready to support this pattern.
- Both `Subscribe` overloads throw and log an error if the channel was not created via `AddChannel`; if this is not strictly required by design, consider either auto-creating channels on first use or returning a clearer, non-logged failure path to avoid noisy logs and hard-to-diagnose runtime errors.
- When no handlers are found in `_CallEventHandlerAsync`, the method always logs an error; if "no subscriber" is a normal situation for some events, consider downgrading this to a debug/trace-level log or making it configurable to avoid excessive error logging in typical flows.

## Individual Comments

### Comment 1
<location path="PCL.Core/App/EventBus/EventBusService.cs" line_range="70" />
<code_context>
+        }
+
+        var dataType = typeof(TEventData);
+        var handlers = dataHandler.GetOrAdd(dataType, _ => new ConcurrentDictionary<Guid, (Func<EventDataBase, Task>, WeakReference<object>?, bool)>());
+
+        var ownerRef = new WeakReference<object>(handler);
</code_context>
<issue_to_address>
**suggestion:** The `OwnsOwner` flag in the handler tuple is never read and can be removed or wired into the disposal logic.

The handler dictionary stores a `(Func<EventDataBase, Task>, WeakReference<object>?, bool OwnsOwner)` tuple, but this flag is never read; disposal is based only on `disposeOwnerOnUnsubscribe` in the closure. Please either remove `OwnsOwner` from the tuple and its assignments, or use it in the disposal logic so the stored state matches the actual behavior.

Suggested implementation:

```csharp
        var dataType = typeof(TEventData);
        var handlers = dataHandler.GetOrAdd(dataType, _ => new ConcurrentDictionary<Guid, (Func<EventDataBase, Task>, WeakReference<object>?)>());

```

```csharp
        var id = Guid.NewGuid();
        handlers.TryAdd(id, (Wrapper, ownerRef));

```

There are likely other declarations/usages of this handler dictionary in `EventBusService.cs` (e.g., where the dictionary field is declared or iterated). Update all such occurrences to use the 2-tuple `(Func<EventDataBase, Task>, WeakReference<object>?)` instead of the 3-tuple with the `bool` flag, and remove any tuple deconstruction or access that refers to the `OwnsOwner` element.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
帮助我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • The comment on IResponsibleEventHandler<TResponse> says it is obsolete but the interface itself is not marked as such; consider adding an [Obsolete] attribute with a clear message or removing it until you are ready to support this pattern.
  • Both Subscribe overloads throw and log an error if the channel was not created via AddChannel; if this is not strictly required by design, consider either auto-creating channels on first use or returning a clearer, non-logged failure path to avoid noisy logs and hard-to-diagnose runtime errors.
  • When no handlers are found in _CallEventHandlerAsync, the method always logs an error; if "no subscriber" is a normal situation for some events, consider downgrading this to a debug/trace-level log or making it configurable to avoid excessive error logging in typical flows.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The comment on `IResponsibleEventHandler<TResponse>` says it is obsolete but the interface itself is not marked as such; consider adding an `[Obsolete]` attribute with a clear message or removing it until you are ready to support this pattern.
- Both `Subscribe` overloads throw and log an error if the channel was not created via `AddChannel`; if this is not strictly required by design, consider either auto-creating channels on first use or returning a clearer, non-logged failure path to avoid noisy logs and hard-to-diagnose runtime errors.
- When no handlers are found in `_CallEventHandlerAsync`, the method always logs an error; if "no subscriber" is a normal situation for some events, consider downgrading this to a debug/trace-level log or making it configurable to avoid excessive error logging in typical flows.

## Individual Comments

### Comment 1
<location path="PCL.Core/App/EventBus/EventBusService.cs" line_range="70" />
<code_context>
+        }
+
+        var dataType = typeof(TEventData);
+        var handlers = dataHandler.GetOrAdd(dataType, _ => new ConcurrentDictionary<Guid, (Func<EventDataBase, Task>, WeakReference<object>?, bool)>());
+
+        var ownerRef = new WeakReference<object>(handler);
</code_context>
<issue_to_address>
**suggestion:** The `OwnsOwner` flag in the handler tuple is never read and can be removed or wired into the disposal logic.

The handler dictionary stores a `(Func<EventDataBase, Task>, WeakReference<object>?, bool OwnsOwner)` tuple, but this flag is never read; disposal is based only on `disposeOwnerOnUnsubscribe` in the closure. Please either remove `OwnsOwner` from the tuple and its assignments, or use it in the disposal logic so the stored state matches the actual behavior.

Suggested implementation:

```csharp
        var dataType = typeof(TEventData);
        var handlers = dataHandler.GetOrAdd(dataType, _ => new ConcurrentDictionary<Guid, (Func<EventDataBase, Task>, WeakReference<object>?)>());

```

```csharp
        var id = Guid.NewGuid();
        handlers.TryAdd(id, (Wrapper, ownerRef));

```

There are likely other declarations/usages of this handler dictionary in `EventBusService.cs` (e.g., where the dictionary field is declared or iterated). Update all such occurrences to use the 2-tuple `(Func<EventDataBase, Task>, WeakReference<object>?)` instead of the 3-tuple with the `bool` flag, and remove any tuple deconstruction or access that refers to the `OwnsOwner` element.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread PCL.Core/App/EventBus/EventBusService.cs Outdated
Copy link
Copy Markdown
Member

@MoYuan-CN MoYuan-CN left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

看起来应该没啥大问题了

@pcl-ce-automation pcl-ce-automation Bot added 🕑 等待合并 已处理完毕,正在等待代码合并入主分支 and removed 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查 labels May 23, 2026
@whitecat346 whitecat346 merged commit 5beccc7 into PCL-Community:dev May 23, 2026
3 checks passed
@pcl-ce-automation pcl-ce-automation Bot added 👌 完成 相关问题已修复或功能已实现,计划在下次版本更新时正式上线 and removed 🕑 等待合并 已处理完毕,正在等待代码合并入主分支 labels May 23, 2026
@whitecat346 whitecat346 deleted the feat/eventbus branch May 23, 2026 08:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: L PR 大小评估:大型 👌 完成 相关问题已修复或功能已实现,计划在下次版本更新时正式上线

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants