Unity实现支持泛型的事件管理以减少使用object作为参数带来的频繁装拆箱

如果不用C#自身的event关键字而是要自己实现一个可统一管理游戏中各种消息事件通知管理的系统模块EventManger时,通常都是把事件delegate的参数定义为object类型以适应所有的数据类型,然而这样做的后果就是在使用过程中存在很频繁的装拆箱操作。
实际是有办法实现支持泛型的事件管理的,关键点在于所有形式的delegate方法都是可以保存在类型为Delegate的变量上的,保存和调用时将Delegate强转为目标delegate就行了。简单示例如下:

 1     public delegate void Act ();
 2     public delegate void Act<T, U>(T t, U u);
 3 
 4 
 5     Dictionary<int, Delegate> eventTable = new Dictionary<int, Delegate>();
 6 
 7     public void AddListener<T, U>(int eventType, Act<T, U> listenerBeingAdded)
 8     {
 9         if (!eventTable.ContainsKey(eventType))
10         {
11             eventTable.Add(eventType, null);
12         }
13 
14         Delegate d = eventTable[eventType];
15         if (d != null && d.GetType() != listenerBeingAdded.GetType())
16         {
17             Debug.LogError(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, d.GetType().Name, listenerBeingAdded.GetType().Name));
18         }
19         else
20         {
21             eventTable[eventType] = (Act<T, U>)eventTable[eventType] + listenerBeingAdded;
22         }
23     }
24 
25     public void RemoveListen<T, U>(int eventType, Act<T, U> listenerBeingRemoved)
26     {
27         if (eventTable.ContainsKey(eventType))
28         {
29             Delegate d = eventTable[eventType];
30 
31             if (d == null)
32             {
33                 Debug.LogError(string.Format("Attempting to remove listener with for event type \"{0}\" but current listener is null.", eventType));
34             }
35             else if (d.GetType() != listenerBeingRemoved.GetType())
36             {
37                 Debug.LogError(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", eventType, d.GetType().Name, listenerBeingRemoved.GetType().Name));
38             }
39             else
40             {
41                 eventTable[eventType] = (Act<T, U>)eventTable[eventType] - listenerBeingRemoved;
42                 if (eventTable[eventType] == null)
43                 {
44                     eventTable.Remove(eventType);
45                 }
46             }
47         }
48         else
49         {
50             Debug.LogError(string.Format("Attempting to remove listener for type \"{0}\" but Messenger doesn't know about this event type.", eventType));
51         }
52     }
53 
54     public void Dispatch<T, U>(int eventType, T param1, U param2)
55     {
56         Delegate d;
57         if (eventTable.TryGetValue(eventType, out d))
58         {
59             ((Act<T, U>)d)(param1, param2);
60         }
61     }
62 
63     [ContextMenu("Test")]
64     void Test ()
65     {
66         AddListener<int, string>(1, MyCallback);
67         Dispatch(1, 22, "333");
68         RemoveListen<int, string>(1, MyCallback);
69     }
70 
71     private void MyCallback (int n, string s)
72     {
73         Debug.Log(string.Format("param1 {0}, parma2 {1}", n, s));
74     }

 

预定义多个不同参数个数的delegate,再分别重载几个Add、Remove、Dispatch支持不同类型delegate的方法就可以实现整套支持不同参数类型不同参数个数的消息管理功能了。

 

以上方法可以完全避免参数传递之间的拆装箱,但是稍微有点麻烦之处在于需要重载很多Add、Remove、Dispatch函数。有个简单点的作法是直接将Delegate作为这三个函数的参数而不是具体的delegate,但调用时直接传入具名函数是不能自动转换为Delegate的,需要对每个delegate作个简单的封装,具体如下:

 1     public delegate void Act ();
 2     public delegate void Act<T, U>(T t, U u);
 3 
 4 
 5     Dictionary<int, Delegate> eventTable = new Dictionary<int, Delegate>();
 6 
 7     public void AddListener (int eventType, Delegate listenerBeingAdded)
 8     {
 9         if (!eventTable.ContainsKey(eventType))
10         {
11             eventTable.Add(eventType, null);
12         }
13         Delegate d = eventTable[eventType];
14         if (d != null && d.GetType() != listenerBeingAdded.GetType())
15         {
16             Debug.LogError(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, d.GetType(), listenerBeingAdded.GetType()));
17         }
18         else
19         {
20             eventTable[eventType] = Delegate.Combine(eventTable[eventType], listenerBeingAdded);
21         }
22     }
23 
24     public void RemoveListen (int eventType, Delegate listenerBeingRemoved)
25     {
26         if (eventTable.ContainsKey(eventType))
27         {
28             Delegate d = eventTable[eventType];
29             if (d == null)
30             {
31                 Debug.LogError(string.Format("Attempting to remove listener with for event type \"{0}\" but current listener is null.", eventType));
32             }
33             else if (d.GetType() != listenerBeingRemoved.GetType())
34             {
35                 Debug.LogError(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", eventType, d.GetType().Name, listenerBeingRemoved.GetType().Name));
36             }
37             else
38             {
39                 eventTable[eventType] = Delegate.Remove(eventTable[eventType], listenerBeingRemoved);
40                 if (eventTable[eventType] == null)
41                 {
42                     eventTable.Remove(eventType);
43                 }
44             }
45         }
46         else
47         {
48             Debug.LogError(string.Format("Attempting to remove listener for type \"{0}\" but Messenger doesn't know about this event type.", eventType));
49         }
50     }
51 
52     public void Dispatch (int eventType, params object[] n)
53     {
54         Delegate d;
55         if (eventTable.TryGetValue(eventType, out d))
56         {
57             d.DynamicInvoke(n);
58         }
59     }
60 
61     [ContextMenu("Test")]
62     void Test ()
63     {
64         AddListener(1, ToDelegate<int, string>(MyCallback));
65         AddListener(1, ToDelegate(MyCallback1));
66         Dispatch(1, 22, "333");
67         RemoveListen(1, ToDelegate<int, string>(MyCallback));
68     }
69 
70     private Act<T, U> ToDelegate<T, U>(Act<T, U> act) { return act; }
71     private Act ToDelegate(Act act) { return act; }
72 
73     private void MyCallback (int n, string s)
74     {
75         Debug.Log(string.Format("param1 {0}, parma2 {1}", n, s));
76     }
77 
78     private void MyCallback1 ()
79     {
80         Debug.Log("no param");
81     }

此方法就需要对每个Delegate写个简单的Getter函数,让调用者自己给出具体的函数类别。相比方法1可以省去不少Add、Remove、Dispatch重载代码,但调用者调用时变得麻烦一些,同时由于Dispatch只能接受object[]参数导致了拆装箱,故还是推荐方法1。

转载于:https://www.cnblogs.com/suoluo/p/7439944.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值