Skip to content

[EventInterface]监听侧相关建议和疑问 #289

Description

@SaberArtoriaFan

背景

我在项目中大量使用 [EventInterface] 接口事件,非常喜欢这个设计:一个接口声明就能同时产出事件 ID 常量、分发实现和类型安全的发送入口,是我们见过最干净的事件方案之一。

不过我注意到,发送侧和监听侧的自动化程度是不对称的。

发送侧:完全类型安全

GameEvent.Get<ITimeCountDown>().OnTimeCountDownChange(timer, timeLong);   // 签名由编译器保证

监听侧:退化为「裸 int + 手写泛型」

GameEvent.AddEventListener<float, float>(ITimeCountDown_Event.OnTimeCountDownChange, OnTimeCountDownChange);

这里的 <float, float> 需要人肉与接口方法签名对齐。框架提供了 GameEventAnalyzer(EVENT001/002)来兜底,很感谢这个设计,但它属于事后纠错——实际流程是「写错 → 编译报错 → 跑 CodeFix 修」,而不是「写不错」。

想请教的问题

监听侧没有做成生成式的强类型 API,是有意的设计取舍,还是暂时没排上日程?

猜测可能存在以下考量,想确认一下:

  1. GameEvent.Get<T>() 的语义是「取发送端包装器」,不希望它同时承担监听职责?
  2. 扩展方法会污染接口类型的补全列表,影响 GameEvent.Get<IXxx>(). 之后的输入体验?
  3. 希望保持 AddEventListener 作为唯一注册入口,便于 GameEventMgr / AddUIEvent 统一做生命周期管理?
  4. 纯粹是优先级问题?

建议方案(供参考,不一定是最优解)

在 EventInterfaceGenerator 中,除了现有的 {Iface}_Event 与 {Iface}_Gen,再为每个接口生成一个扩展方法类:

// 生成:ITimeCountDown_EventExt.g.cs
public static class ITimeCountDown_EventExt
{
    public static bool AddListener_OnTimeCountDownChange(this ITimeCountDown _, Action<float, float> handler)
        => GameEvent.AddEventListener(ITimeCountDown_Event.OnTimeCountDownChange, handler);

    public static void RemoveListener_OnTimeCountDownChange(this ITimeCountDown _, Action<float, float> handler)
        => GameEvent.RemoveEventListener(ITimeCountDown_Event.OnTimeCountDownChange, handler);
}

调用侧:

GameEvent.Get<ITimeCountDown>().AddListener_OnTimeCountDownChange(OnTimeCountDownChange);
GameEvent.Get<ITimeCountDown>().RemoveListener_OnTimeCountDownChange(OnTimeCountDownChange);

零参方法走 Action 重载,_Gen / _Event / AddEventListener 全部保持不变。

我们认为收益是:

  • 泛型参数由生成器写死,handler 签名不匹配直接是编译错误,不再依赖 Analyzer 兜底
  • 零运行时开销(纯静态转发,无反射、无装箱)
  • 注册入口统一到 GameEvent.Get<IXxx>(),IDE 补全即可发现
  • 与现有 AddEventListener 完全共存,可逐个迁移,无破坏性变更

我已经在自己的 fork 里准备实现这个改动。如果官方认可这个方向,我很乐意整理成 PR 提交;如果这条路与框架的设计意图冲突,也想请指教原因,好调整自己的扩展方式,避免和后续官方演进打架。

附:使用中发现的其他几个小问题(不确定是否已知,需要的话我可以拆成独立 issue 或直接提 PR)
  1. GameEventAnalyzer 的 CheckMethodNameList 只覆盖 AddEventListener / AddUIEvent,RemoveEventListener 与 GameEventMgr.AddEvent 未被检查。注册写对、解绑写错时编译器不报错,运行时才 Log.Fatal("Delete handle failed")。
  2. GameEventAnalyzer.cs:123-126,接口方法找不到时静默 return,事件名拼错不会有任何提示。
  3. Analyzer 的 Definition.CommonNamespaces 写死 "GameLogic",接口不在该命名空间时匹配失败。
  4. EventInterfaceGenerator.cs:37-42 只处理 NamespaceDeclarationSyntax,文件作用域命名空间(namespace GameLogic;)会导致 fullName 退化成裸接口名;接口无命名空间时 Aggregate 会抛 InvalidOperationException。另外接口继承(interface IA : IB)与非 void 方法会导致生成的 _Gen 编译失败(CS0535),报错指向生成代码,不易定位。
  5. EventMgr.cs:55 的 RegWrapInterface 用的是 _eventEntryMap.Add,二次调用 GameEventHelper.Init() 会抛 duplicate key;EventDelegateData.AddHandler 对重复注册是 Log.Fatal 而非幂等,热重载场景容易触发。
  6. EEventGroup 目前似乎没有被任何代码消费(只为 [EventInterface] 提供分组入参)。
  7. EventInterfaceGenerator 用的是已废弃的非增量 ISourceGenerator,在较大的热更程序集上每次击键都会全量扫描语法树,IDE 响应会有可感知的开销。

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions