# 事件系统设计 **本文引用的文件** - [EasyEvent.cs](file://GFramework.Core/events/EasyEvent.cs) - [EasyEventGeneric.cs](file://GFramework.Core/events/EasyEventGeneric.cs) - [EventBus.cs](file://GFramework.Core/events/EventBus.cs) - [EasyEvents.cs](file://GFramework.Core/events/EasyEvents.cs) - [ArchitectureEvents.cs](file://GFramework.Core/events/ArchitectureEvents.cs) - [OrEvent.cs](file://GFramework.Core/events/OrEvent.cs) - [DefaultUnRegister.cs](file://GFramework.Core/events/DefaultUnRegister.cs) - [UnRegisterList.cs](file://GFramework.Core/events/UnRegisterList.cs) - [IEvent.cs](file://GFramework.Core.Abstractions/events/IEvent.cs) - [IEventBus.cs](file://GFramework.Core.Abstractions/events/IEventBus.cs) - [OrEventExtensions.cs](file://GFramework.Core/extensions/OrEventExtensions.cs) - [UnRegisterListExtension.cs](file://GFramework.Core/extensions/UnRegisterListExtension.cs) - [SettingsChangedEvent.cs](file://GFramework.Game/setting/events/SettingsChangedEvent.cs) - [SettingsBatchChangedEvent.cs](file://GFramework.Game/setting/events/SettingsBatchChangedEvent.cs) - [EventTests.cs](file://GFramework.Core.Tests/events/EventTests.cs) ## 目录 1. [引言](#引言) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖分析](#依赖分析) 7. [性能考虑](#性能考虑) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录:事件命名与结构最佳实践](#附录事件命名与结构最佳实践) ## 引言 本文件面向GFramework的事件系统,系统性阐述事件命名规范、事件结构设计以及事件处理职责分配的最佳实践;并结合仓库中的具体实现,给出类型安全的事件结构、单一职责的事件处理器设计、性能优化建议与测试与调试策略。读者无需深入源码即可理解并应用这些实践。 ## 项目结构 事件系统位于GFramework.Core/events目录,围绕“事件类型 + 事件总线 + 注销机制”构建,同时提供组合事件与全局事件管理能力。游戏层在GFramework.Game/setting/events中提供了领域事件示例(设置变更事件),体现事件在业务场景中的落地方式。 ```mermaid graph TB subgraph "核心事件模块(GFramework.Core)" A["EasyEvent.cs"] B["EasyEventGeneric.cs"] C["EventBus.cs"] D["EasyEvents.cs"] E["OrEvent.cs"] F["DefaultUnRegister.cs"] G["UnRegisterList.cs"] H["IEvent.cs"] I["IEventBus.cs"] J["OrEventExtensions.cs"] K["UnRegisterListExtension.cs"] end subgraph "游戏层事件(GFramework.Game)" L["SettingsChangedEvent.cs"] M["SettingsBatchChangedEvent.cs"] end C --> D C --> B D --> B E --> F J --> E K --> G L --> I M --> I ``` **图表来源** - [EventBus.cs](file://GFramework.Core/events/EventBus.cs#L1-L55) - [EasyEvents.cs](file://GFramework.Core/events/EasyEvents.cs#L1-L85) - [EasyEventGeneric.cs](file://GFramework.Core/events/EasyEventGeneric.cs#L1-L123) - [OrEvent.cs](file://GFramework.Core/events/OrEvent.cs#L1-L57) - [DefaultUnRegister.cs](file://GFramework.Core/events/DefaultUnRegister.cs#L1-L22) - [UnRegisterList.cs](file://GFramework.Core/events/UnRegisterList.cs#L1-L37) - [IEvent.cs](file://GFramework.Core.Abstractions/events/IEvent.cs#L1-L16) - [IEventBus.cs](file://GFramework.Core.Abstractions/events/IEventBus.cs#L1-L37) - [OrEventExtensions.cs](file://GFramework.Core/extensions/OrEventExtensions.cs#L1-L21) - [UnRegisterListExtension.cs](file://GFramework.Core/extensions/UnRegisterListExtension.cs#L1-L32) - [SettingsChangedEvent.cs](file://GFramework.Game/setting/events/SettingsChangedEvent.cs#L1-L32) - [SettingsBatchChangedEvent.cs](file://GFramework.Game/setting/events/SettingsBatchChangedEvent.cs#L1-L31) **章节来源** - [EventBus.cs](file://GFramework.Core/events/EventBus.cs#L1-L55) - [EasyEvents.cs](file://GFramework.Core/events/EasyEvents.cs#L1-L85) - [EasyEventGeneric.cs](file://GFramework.Core/events/EasyEventGeneric.cs#L1-L123) - [OrEvent.cs](file://GFramework.Core/events/OrEvent.cs#L1-L57) - [DefaultUnRegister.cs](file://GFramework.Core/events/DefaultUnRegister.cs#L1-L22) - [UnRegisterList.cs](file://GFramework.Core/events/UnRegisterList.cs#L1-L37) - [IEvent.cs](file://GFramework.Core.Abstractions/events/IEvent.cs#L1-L16) - [IEventBus.cs](file://GFramework.Core.Abstractions/events/IEventBus.cs#L1-L37) - [OrEventExtensions.cs](file://GFramework.Core/extensions/OrEventExtensions.cs#L1-L21) - [UnRegisterListExtension.cs](file://GFramework.Core/extensions/UnRegisterListExtension.cs#L1-L32) - [SettingsChangedEvent.cs](file://GFramework.Game/setting/events/SettingsChangedEvent.cs#L1-L32) - [SettingsBatchChangedEvent.cs](file://GFramework.Game/setting/events/SettingsBatchChangedEvent.cs#L1-L31) ## 核心组件 - 事件接口与基础事件 - IEvent:定义无参事件注册接口,便于统一处理。 - EasyEvent:无参事件,支持注册/注销/触发。 - Event、Event:单/双参数泛型事件,显式实现IEvent以支持无参订阅。 - 事件总线与全局事件管理 - IEventBus:定义Send/Register/UnRegister接口。 - EventBus:基于类型分发的事件总线,内部使用EasyEvents管理事件实例。 - EasyEvents:全局事件容器,按类型缓存事件实例,支持Get/GetOrAdd/Add。 - 组合与注销 - OrEvent:事件“或”组合,任一子事件触发即触发自身。 - DefaultUnRegister:一次性注销器,执行回调并清理引用。 - UnRegisterList:批量注销器,集中管理多个注销器。 - 游戏层事件示例 - SettingsChangedEvent:类型化设置变更事件。 - SettingsBatchChangedEvent:批量设置变更事件。 **章节来源** - [IEvent.cs](file://GFramework.Core.Abstractions/events/IEvent.cs#L1-L16) - [EasyEvent.cs](file://GFramework.Core/events/EasyEvent.cs#L1-L39) - [EasyEventGeneric.cs](file://GFramework.Core/events/EasyEventGeneric.cs#L1-L123) - [IEventBus.cs](file://GFramework.Core.Abstractions/events/IEventBus.cs#L1-L37) - [EventBus.cs](file://GFramework.Core/events/EventBus.cs#L1-L55) - [EasyEvents.cs](file://GFramework.Core/events/EasyEvents.cs#L1-L85) - [OrEvent.cs](file://GFramework.Core/events/OrEvent.cs#L1-L57) - [DefaultUnRegister.cs](file://GFramework.Core/events/DefaultUnRegister.cs#L1-L22) - [UnRegisterList.cs](file://GFramework.Core/events/UnRegisterList.cs#L1-L37) - [SettingsChangedEvent.cs](file://GFramework.Game/setting/events/SettingsChangedEvent.cs#L1-L32) - [SettingsBatchChangedEvent.cs](file://GFramework.Game/setting/events/SettingsBatchChangedEvent.cs#L1-L31) ## 架构总览 事件系统采用“类型驱动 + 全局容器 + 总线分发”的架构。事件通过类型标识,由EventBus统一调度;EasyEvents负责事件实例的缓存与复用;OrEvent支持事件组合;DefaultUnRegister/UnRegisterList保障生命周期管理。 ```mermaid classDiagram class IEvent { +Register(onEvent) IUnRegister } class EasyEvent { +Register(onEvent) IUnRegister +UnRegister(onEvent) void +Trigger() void } class Event_T_ { +Register(onEvent) IUnRegister +UnRegister(onEvent) void +Trigger(t) void +Register(Action) IUnRegister } class Event_T_TK_ { +Register(onEvent) IUnRegister +UnRegister(onEvent) void +Trigger(t, tk) void +Register(Action) IUnRegister } class IEventBus { +Send~T~() void +Send~T~(e) void +Register~T~(onEvent) IUnRegister +UnRegister~T~(onEvent) void } class EventBus { -EasyEvents _mEvents +Send~T~() void +Send~T~(e) void +Register~T~(onEvent) IUnRegister +UnRegister~T~(onEvent) void } class EasyEvents { -Dictionary~Type,IEvent~ _mTypeEvents +Get~T~() T +Register~T~() void +GetOrAdd~T~() T +AddEvent~T~() void +GetEvent~T~() T +GetOrAddEvent~T~() T } class OrEvent { +Or(event) OrEvent +Register(onEvent) IUnRegister +UnRegister(onEvent) void -Trigger() void } class DefaultUnRegister { +UnRegister() void } class UnRegisterList { +Add(unRegister) void +UnRegisterAll() void } IEvent <|.. EasyEvent IEvent <|.. Event_T_ IEvent <|.. Event_T_TK_ IEventBus <|.. EventBus EventBus --> EasyEvents : "使用" OrEvent --> DefaultUnRegister : "组合" UnRegisterList --> DefaultUnRegister : "管理" ``` **图表来源** - [IEvent.cs](file://GFramework.Core.Abstractions/events/IEvent.cs#L1-L16) - [EasyEvent.cs](file://GFramework.Core/events/EasyEvent.cs#L1-L39) - [EasyEventGeneric.cs](file://GFramework.Core/events/EasyEventGeneric.cs#L1-L123) - [IEventBus.cs](file://GFramework.Core.Abstractions/events/IEventBus.cs#L1-L37) - [EventBus.cs](file://GFramework.Core/events/EventBus.cs#L1-L55) - [EasyEvents.cs](file://GFramework.Core/events/EasyEvents.cs#L1-L85) - [OrEvent.cs](file://GFramework.Core/events/OrEvent.cs#L1-L57) - [DefaultUnRegister.cs](file://GFramework.Core/events/DefaultUnRegister.cs#L1-L22) - [UnRegisterList.cs](file://GFramework.Core/events/UnRegisterList.cs#L1-L37) ## 详细组件分析 ### 事件接口与基础事件 - 设计要点 - IEvent仅暴露无参注册,便于统一处理与扩展。 - EasyEvent提供最简事件模型,适合广播型通知。 - Event/Event显式实现IEvent.Register(Action),允许“无参订阅”,同时保留强类型参数触发。 - 使用建议 - 优先使用Event进行领域事件建模,确保参数类型安全。 - 无参事件仅用于系统级广播,避免承载复杂状态。 **章节来源** - [IEvent.cs](file://GFramework.Core.Abstractions/events/IEvent.cs#L1-L16) - [EasyEvent.cs](file://GFramework.Core/events/EasyEvent.cs#L1-L39) - [EasyEventGeneric.cs](file://GFramework.Core/events/EasyEventGeneric.cs#L1-L123) ### 事件总线与全局事件管理 - 设计要点 - EventBus基于类型分发,Send()自动构造事件实例,Send(e)直接触发实例。 - 内部使用EasyEvents缓存事件实例,避免重复创建。 - Register/UnRegister通过类型键控,保证事件隔离。 - 使用建议 - 事件类型应具备无参构造函数以便自动构造。 - 对于频繁触发的事件,优先使用EventBus.Send(e)传入实例,减少构造开销。 **章节来源** - [EventBus.cs](file://GFramework.Core/events/EventBus.cs#L1-L55) - [EasyEvents.cs](file://GFramework.Core/events/EasyEvents.cs#L1-L85) ### 组合事件与注销机制 - 设计要点 - OrEvent将多个事件“或”组合,任一子事件触发即触发自身;通过扩展方法Or(IEvent)链式组合。 - DefaultUnRegister封装一次性的注销回调,避免重复注销。 - UnRegisterList集中管理多个注销器,提供批量注销能力。 - 使用建议 - 使用OrEvent简化多事件聚合场景(如“任意加载完成”)。 - 在对象生命周期结束时调用UnRegisterAll,确保资源回收。 **章节来源** - [OrEvent.cs](file://GFramework.Core/events/OrEvent.cs#L1-L57) - [OrEventExtensions.cs](file://GFramework.Core/extensions/OrEventExtensions.cs#L1-L21) - [DefaultUnRegister.cs](file://GFramework.Core/events/DefaultUnRegister.cs#L1-L22) - [UnRegisterList.cs](file://GFramework.Core/events/UnRegisterList.cs#L1-L37) - [UnRegisterListExtension.cs](file://GFramework.Core/extensions/UnRegisterListExtension.cs#L1-L32) ### 游戏层事件示例 - 设计要点 - SettingsChangedEvent携带类型化设置实例与变更时间,便于处理器按类型处理。 - SettingsBatchChangedEvent表示批量变更,适合聚合处理。 - 使用建议 - 领域事件尽量携带必要上下文(如时间戳、来源),提升可观测性。 - 批量事件用于降低事件风暴,提高处理效率。 **章节来源** - [SettingsChangedEvent.cs](file://GFramework.Game/setting/events/SettingsChangedEvent.cs#L1-L32) - [SettingsBatchChangedEvent.cs](file://GFramework.Game/setting/events/SettingsBatchChangedEvent.cs#L1-L31) ### 事件序列与调用流程 以下序列图展示了EventBus.Send()的典型调用路径。 ```mermaid sequenceDiagram participant Sender as "发送方" participant Bus as "EventBus" participant Events as "EasyEvents" participant EventT as "Event" participant Handler as "事件处理器" Sender->>Bus : "Send() 或 Send(e)" Bus->>Events : "GetOrAddEvent>()" Events-->>Bus : "Event 实例" Bus->>EventT : "Trigger(实例或new T())" EventT->>Handler : "回调执行" Handler-->>EventT : "处理完成" EventT-->>Bus : "全部回调完成" Bus-->>Sender : "发送完成" ``` **图表来源** - [EventBus.cs](file://GFramework.Core/events/EventBus.cs#L1-L55) - [EasyEvents.cs](file://GFramework.Core/events/EasyEvents.cs#L1-L85) - [EasyEventGeneric.cs](file://GFramework.Core/events/EasyEventGeneric.cs#L1-L123) ## 依赖分析 - 组件耦合 - EventBus依赖EasyEvents与Event族,形成“总线-容器-事件”的分层。 - OrEvent依赖DefaultUnRegister,UnRegisterList管理多个注销器。 - 外部接口 - IEvent/IEventBus提供抽象契约,便于替换实现与测试。 - 循环依赖 - 未发现循环依赖;事件类型通过类型键控,避免运行时耦合。 ```mermaid graph LR IEvent["IEvent 接口"] --> EasyEvent["EasyEvent"] IEvent --> EventT["Event"] IEvent --> EventTTK["Event"] IEventBus["IEventBus 接口"] --> EventBus["EventBus"] EventBus --> EasyEvents["EasyEvents"] EventBus --> EventT OrEvent["OrEvent"] --> DefaultUnRegister["DefaultUnRegister"] UnRegisterList["UnRegisterList"] --> DefaultUnRegister ``` **图表来源** - [IEvent.cs](file://GFramework.Core.Abstractions/events/IEvent.cs#L1-L16) - [EasyEvent.cs](file://GFramework.Core/events/EasyEvent.cs#L1-L39) - [EasyEventGeneric.cs](file://GFramework.Core/events/EasyEventGeneric.cs#L1-L123) - [IEventBus.cs](file://GFramework.Core.Abstractions/events/IEventBus.cs#L1-L37) - [EventBus.cs](file://GFramework.Core/events/EventBus.cs#L1-L55) - [EasyEvents.cs](file://GFramework.Core/events/EasyEvents.cs#L1-L85) - [OrEvent.cs](file://GFramework.Core/events/OrEvent.cs#L1-L57) - [DefaultUnRegister.cs](file://GFramework.Core/events/DefaultUnRegister.cs#L1-L22) - [UnRegisterList.cs](file://GFramework.Core/events/UnRegisterList.cs#L1-L37) **章节来源** - [IEvent.cs](file://GFramework.Core.Abstractions/events/IEvent.cs#L1-L16) - [IEventBus.cs](file://GFramework.Core.Abstractions/events/IEventBus.cs#L1-L37) - [EventBus.cs](file://GFramework.Core/events/EventBus.cs#L1-L55) - [EasyEvents.cs](file://GFramework.Core/events/EasyEvents.cs#L1-L85) - [OrEvent.cs](file://GFramework.Core/events/OrEvent.cs#L1-L57) - [DefaultUnRegister.cs](file://GFramework.Core/events/DefaultUnRegister.cs#L1-L22) - [UnRegisterList.cs](file://GFramework.Core/events/UnRegisterList.cs#L1-L37) ## 性能考虑 - 事件过滤 - 使用EventBus.Send(e)传入实例,避免每次触发都构造新事件对象。 - 对高频事件,建议复用事件实例并更新字段,减少GC压力。 - 批量处理 - 使用UnRegisterList集中注销,避免逐个注销带来的遍历成本。 - 使用OrEvent组合多个事件,减少多次订阅与触发的成本。 - 内存管理 - 事件处理器应尽快UnRegister,防止闭包捕获导致的长生命周期对象滞留。 - 对长生命周期对象,避免在事件处理器中持有强引用。 - 并发与线程 - 事件回调在触发时同步执行,避免跨线程访问共享状态;如需异步处理,请在回调内调度至目标线程。 [本节为通用性能建议,不直接分析具体文件] ## 故障排查指南 - 常见问题 - 回调未触发:检查是否正确注册、是否被提前UnRegister、事件类型是否匹配。 - 内存泄漏:确认处理器在对象销毁时调用UnRegisterAll或单独UnRegister。 - 事件风暴:合并为批量事件或引入节流/去抖策略。 - 调试技巧 - 在事件处理器中记录上下文(如时间戳、来源),便于定位问题。 - 使用单元测试验证事件注册/注销/触发的正确性。 - 单元测试参考 - 测试覆盖了EasyEvent与Event/Event的注册、注销与多处理器调用行为,可作为编写自测的模板。 **章节来源** - [EventTests.cs](file://GFramework.Core.Tests/events/EventTests.cs#L1-L164) ## 结论 GFramework的事件系统以类型安全为核心,通过EventBus与EasyEvents实现高效分发与复用;OrEvent与注销机制保障了组合与生命周期管理。遵循本文的命名与结构规范、单一职责的处理器设计、以及性能与测试策略,可在复杂游戏中稳定地构建可维护的事件体系。 [本节为总结性内容,不直接分析具体文件] ## 附录:事件命名与结构最佳实践 ### 事件命名规范 - 无参事件:使用动词短语,如“系统就绪”、“开始销毁”等,体现广播性质。 - 示例:ArchitectureLifecycleReadyEvent、ArchitectureDestroyingEvent、ArchitectureDestroyedEvent、ArchitectureFailedInitializationEvent - 领域事件:使用“名词+动作+对象”的结构,明确事件主体与影响对象。 - 示例:PlayerHealthChangedEvent、PlayerDiedEvent、LevelCompletedEvent、SettingsChangedEvent - 批量事件:描述聚合行为,强调“批量”而非单点。 - 示例:SettingsBatchChangedEvent **章节来源** - [ArchitectureEvents.cs](file://GFramework.Core/events/ArchitectureEvents.cs#L1-L31) - [SettingsChangedEvent.cs](file://GFramework.Game/setting/events/SettingsChangedEvent.cs#L1-L32) - [SettingsBatchChangedEvent.cs](file://GFramework.Game/setting/events/SettingsBatchChangedEvent.cs#L1-L31) ### 事件结构设计 - 类型安全 - 使用Event或Event承载参数,避免使用object或动态类型。 - 领域事件尽量携带必要上下文(如时间戳、来源、类型信息)。 - 不变性与复用 - 对高频事件,复用事件实例并更新字段,减少GC。 - 避免在事件中保存长生命周期对象的强引用。 - 可观测性 - 为事件添加时间戳与来源标识,便于日志与追踪。 **章节来源** - [EasyEventGeneric.cs](file://GFramework.Core/events/EasyEventGeneric.cs#L1-L123) - [SettingsChangedEvent.cs](file://GFramework.Game/setting/events/SettingsChangedEvent.cs#L1-L32) - [SettingsBatchChangedEvent.cs](file://GFramework.Game/setting/events/SettingsBatchChangedEvent.cs#L1-L31) ### 事件处理职责分配 - 单一职责 - UIHealthBarController:仅处理与健康值显示相关的事件,避免混入音频、逻辑等职责。 - AudioController:仅处理与音效播放相关的事件,避免混入UI逻辑。 - 分层解耦 - 事件总线负责分发,处理器负责具体业务逻辑,避免处理器直接依赖其他处理器。 - 生命周期管理 - 在对象销毁时统一调用UnRegisterAll,确保事件链路完整释放。 **章节来源** - [EventBus.cs](file://GFramework.Core/events/EventBus.cs#L1-L55) - [UnRegisterList.cs](file://GFramework.Core/events/UnRegisterList.cs#L1-L37) ### 事件系统测试策略 - 行为验证 - 验证注册/注销是否生效、多处理器是否均被调用、参数传递是否正确。 - 边界条件 - 空处理器集合、重复注册/注销、并发触发等。 - 回归测试 - 使用EventTests类似的断言模式,覆盖关键事件路径。 **章节来源** - [EventTests.cs](file://GFramework.Core.Tests/events/EventTests.cs#L1-L164)