Unity Events 是一个很好的特性。它们简单、可在调试器中友好,并且对于许多工作流程来说非常有用。但是,在使用它们进行大量项目开发后,我开始遇到越来越难以忽视的问题。 # 1.脆弱的持久性绑定 Unity Events 使用方法名称序列化方法引用。这意味着简单的重构,如从 OpenDoor() 重命名为 UnlockDoor(),会破坏持久性绑定。最糟糕的是,这种失败可以在事件实际触发之前保持隐匿,从而使调试更加困难。 # 2.有限的方法支持 Unity Events 只支持有限的方法签名和参数类型。如果需要调用带有多个参数、自定义类型或更复杂签名的方法,你通常会创建包装方法,只为了使 Unity Events 接受调用。 # 3.没有项目级可见性 随着项目的增长,回答类似问题的难度会越来越大: * 这个事件在哪里使用? * 谁在监听这个事件? * 为什么这个回调没有被触发? * 哪些绑定是断裂的? Unity Events 不提供一种实用的方法来跟踪和诊断这些问题。 # 4.性能开销 Unity Events 的讨论中常常提到它们比 C# 代理慢。然而,平均调用时间并不是唯一重要的因素。第一个调用成本经常被忽略。在我的测试中,单个持久性监听器的第一调用成本已超过 0.05 ms,在目前可用最快的消费者 CPU Ryzen 9 9950X 上。低端硬件上,这个成本显著更高。这种情况是因为持久性调用依赖于反射来解析目标方法,伴随着调用时的额外验证和设置工作。这项测试只使用了一个持久性监听器。在实际项目中,事件通常具有多个持久性监听器,每个监听器都引入了自己的方法解析和初始化成本。 # 为什么我创建了自己的事件系统 Ramdal Events 是为了解决这些限制,同时保留了基于调试器的事件的便利性。它解决了这些问题: * 使用预编译和缓存调用数据而不是反复通过反射解析方法。 * 使用缓存代理以近代理级别的性能。 * 使用唯一的方法 ID 来使持久性绑定重构安全。 * 支持几乎任何方法签名和参数类型。 * 提供项目级事件跟踪和诊断,以快速找到和修复问题。 目标不是简单地创建一个更快的事件系统。目标是创建一个事件系统,使其更容易扩展、维护和调试,当项目变得更加复杂时。