核心是用泛型参数表示事件类型,而非通用object字段;所有事件实现空接口ievent,具体数据由泛型子类如addpublishconfigevent携带,订阅时锁定t类型,确保编译期类型安全、零反射、无字符串匹配。

核心在于让事件类型本身成为泛型参数,而不是把数据塞进一个通用的 object 字段。这样编译器能检查类型,运行时不用反射或字符串匹配,订阅和发布都直接绑定到具体类型。
用 IEvent 契约统一事件基类
所有事件都实现同一个空接口 IEvent,只声明公共契约(比如 IsSuccess、Exception),不带业务字段。真正携带数据的是继承它的泛型事件类,例如:
-
AddPublishConfigEvent
:T 是具体配置类型(如 WordPressConfig) -
SaveDocumentEvent
:T 是文档实体类型(如 MarkdownDoc、HtmlDoc)
这个设计把“事件种类”和“载荷类型”合二为一,避免了用字符串标识事件名带来的类型不安全问题。
订阅时锁定具体泛型类型
订阅方法签名是 SubscribeEvent
- UI 层订阅
SubscribeEvent<addpublishconfigevent>>(e => { ... })</addpublishconfigevent>,就只会收到 WordPress 类型的配置事件 - Core 层发布
PublishEvent(new AddPublishConfigEvent<cnblogsconfig> { Data = config })</cnblogsconfig>,只会触发 Cnblogs 对应的订阅者 - 不同 T 的 AddPublishConfigEvent
在编译期就是完全不同的类型,不会互相干扰
泛型事件类内部封装业务数据
事件类不是空壳,它持有强类型的业务对象。以 AddPublishConfigEvent
public class AddPublishConfigEvent<t> : IEvent
{
public T Data { get; set; }
public bool IsSuccess { get; set; }
public Exception Exception { get; set; }
}</t>
这样 UI 层传入一个 WordPressConfig 实例,Core 层拿到的就是原汁原味的 WordPressConfig,无需转型、无运行时异常风险,IDE 还能自动补全 Data 的成员。
避免“为每个平台建一个事件类”的陷阱
有人会倾向写 AddWordPressPublishConfigEvent、AddCnblogsPublishConfigEvent 等多个非泛型类,但这会导致:
- 每加一个平台就要新增事件类 + 订阅方法 + 处理逻辑,代码量线性增长
- Core 层处理入口分散,难以统一拦截、日志、重试等横切关注点
而泛型方案只需一套通用处理逻辑,靠类型参数区分上下文,扩展新平台只需定义新配置类,事件总线代码零修改。










