如何让实现接口的结构体注册事件回调时避免装箱?
问题描述
背景
我希望让类实例向事件系统注册回调,同时避免装箱操作。
自研的事件系统结构如下:
class EventSystem { // 为事件注册结构体实例 // 事件触发时调用Run()方法并传入信息 public void Register(ICallback callbackStruct, Event e); } public enum Event { SOME_EVENT, SOME_OTHER_EVENT } interface ICallback { void Run(string data); }
示例实现:
class Example { private Behaviour_A a = new Behaviour_A(); private Behaviour_B b = new Behaviour_B(); public Example() { eventSystem.Register(a, SOME_EVENT); eventSystem.Register(b, SOME_OTHER_EVENT); } struct Behaviour_A : ICallback { void Run(string data) { // 自定义逻辑 } } struct Behaviour_B : ICallback { void Run(string data) { // 另一套自定义逻辑 } } }
现有方案优势
- 采用组合而非继承,支持单个实例为同一事件注册多种回调行为
- 结构体相比匿名函数具备状态性,可复用不同参数
现有方案劣势
每次传递结构体到Register方法时都会触发装箱操作,而我需要尽可能避免堆内存分配。
核心问题
有没有办法在保留结构体自定义行为的同时,避免装箱操作?
解决方案
方法1:泛型约束改造事件系统(推荐,支持有状态结构体)
通过为Register方法添加泛型参数,并约束为实现ICallback的结构体,CLR会为每个具体的结构体类型生成专属的方法版本,直接操作值类型实例,完全避免装箱。
改造后的EventSystem实现:
class EventSystem { private Dictionary<Event, ICallbackContainer> _eventCallbacks = new Dictionary<Event, ICallbackContainer>(); public void Register<T>(T callbackStruct, Event e) where T : struct, ICallback { if (!_eventCallbacks.TryGetValue(e, out var container)) { container = new CallbackContainer<T>(); _eventCallbacks[e] = container; } ((CallbackContainer<T>)container).Callbacks.Add(callbackStruct); } public void TriggerEvent(Event e, string data) { if (_eventCallbacks.TryGetValue(e, out var container)) { container.Invoke(data); } } // 抽象容器接口,统一触发逻辑 private interface ICallbackContainer { void Invoke(string data); } // 泛型容器,存储特定结构体类型的回调列表 private class CallbackContainer<T> : ICallbackContainer where T : struct, ICallback { public List<T> Callbacks { get; } = new List<T>(); public void Invoke(string data) { foreach (var callback in Callbacks) { callback.Run(data); } } } }
调用时直接传入结构体实例即可,无需装箱:
eventSystem.Register(a, SOME_EVENT); // a是Behaviour_A结构体实例,无装箱
方法2:委托+结构体静态方法(适用于无状态回调)
如果你的结构体不需要维护状态,可以直接将静态方法包装成Action<string>委托注册,同样不会产生装箱:
class EventSystem { private Dictionary<Event, List<Action<string>>> _eventCallbacks = new Dictionary<Event, List<Action<string>>>(); public void Register(Action<string> callback, Event e) { if (!_eventCallbacks.TryGetValue(e, out var list)) { list = new List<Action<string>>(); _eventCallbacks[e] = list; } list.Add(callback); } public void TriggerEvent(Event e, string data) { if (_eventCallbacks.TryGetValue(e, out var list)) { foreach (var callback in list) { callback(data); } } } } // 定义无状态结构体 struct Behaviour_A { public static void Run(string data) { // 自定义逻辑 } } // 注册方式 eventSystem.Register(Behaviour_A.Run, SOME_EVENT);
但这种方式不支持有状态的结构体,仅适合无状态场景。
方法3:值类型列表优化存储
第一种泛型方案已经通过List<T>直接存储结构体实例,所有操作都在值类型层面进行,完全避免了装箱。如果需要更极致的内存控制,还可以使用Span<T>或Memory<T>来管理回调列表,但对于大多数场景,List<T>已经足够高效。
内容的提问来源于stack exchange,提问作者Jam
相关产品推荐
相关产品推荐

