不过假如我们的 B 脚本注册了非常多的消息,代码会变成如下:
public class B : MonoBehaviour { void Awake() { MsgDispatcher.Register("DO",DoSomething); MsgDispatcher.Register("DO1",MsgReceiver); MsgDispatcher.Register("DO2",MsgReceiver1); MsgDispatcher.Register("DO3",MsgReceiver2); } void DoSomething(object data) { // do something } ... void OnDestroy() { MsgDispatcher.UnRegiter("DO",DoSomething); MsgDispatcher.UnRegiter("DO1",MsgReceiver); MsgDispatcher.UnRegiter("DO2",MsgReceiver1); MsgDispatcher.UnRegiter("DO3",MsgReceiver2); } }每次注册一个消息,对应地,在 OnDestroy 操作的时候就要注销一个事件。这个非常像我们写 C++ 的时候遵循的一个内存管理法则,每次申请内存就要在析构方法里进行释放。
而这样使用消息机制,初学者非常容易忘记消息的注销,从而导致引用异常等等。
那么如何解决呢?
用一个 Dictionary 记录这个脚本中已经注册过的消息,以及消息名对应的回调。
代码如下:
public class B : MonoBehaviour { Dictionary<string,Action<object>> mMsgRegisterRecorder = new Dictionary<string,Action<object>>(); void Awake() { MsgDispatcher.Register("DO",DoSomething); mMsgRegisterRecorder.Add("DO",DoSomething); MsgDispatcher.Register("DO1",MsgReceiver); mMsgRegisterRecorder.Add("DO1",MsgReceiver); MsgDispatcher.Register("DO2",MsgReceiver1); mMsgRegisterRecorder.Add("DO2",MsgReceiver1); MsgDispatcher.Register("DO3",MsgReceiver2); mMsgRegisterRecorder.Add("DO3",MsgReceiver2); } void DoSomething(object data) { // do something } ... void OnDestroy() { foreach (var keyValuePair in mMsgRegisterRecorder) { MsgDispatcher.UnRegister(keyValuePair.Key,keyValuePair.Value); } mMsgRegisterRecorder.Clear(); } }这样,不管注册了多少个消息,只要在 OnDestroy 的时候, 进行一个遍历,这样消息就全部注销掉了。
但是这样写的话注册,就变得麻烦了,每次注册要先两行代码。
MsgDispatcher.Register("DO3",MsgReceiver2); mMsgRegisterRecorder.Add("DO3",MsgReceiver2);把两行提取成一个方法就好了。
提取的方法,代码如下:
而注册消息的代码就会变成如下:
private void Awake() { RegisterMsg("Do",DoSomething); RegisterMsg("DO1",MsgReceiver); RegisterMsg("DO2", _=>{ }); RegisterMsg("DO3", _=>{ }); }是不是精简了很多,而且也可以注册 Lambda 表达式了。
不过我们看下现在的 B 脚本全部代码:
public class B : MonoBehaviour { Dictionary<string, Action<object>> mMsgRegisterRecorder = new Dictionary<string, Action<object>>(); private void Awake() { RegisterMsg("Do",DoSomething); RegisterMsg("DO1",_=>{ }); RegisterMsg("DO2", _=>{ }); RegisterMsg("DO3", _=>{ }); } private void RegisterMsg(string msgName, Action<object> onMsgReceived) { MsgDispatcher.Register(msgName, onMsgReceived); mMsgRegisterRecorder.Add(msgName, onMsgReceived); } void DoSomething(object data) { // do something } private void OnDestroy() { foreach (var keyValuePair in mMsgRegisterRecorder) { MsgDispatcher.UnRegister(keyValuePair.Key,keyValuePair.Value); } mMsgRegisterRecorder.Clear(); } }目前,每个要使用相同消息策略的脚本,都实现如上的代码,会产生很多的重复代码。所以这里我们要开始考虑如何让这个消息注册/注销的策略进行复用。首先用静态方法是不可能了,因为这个策略是有状态的(成员变量)。所以以我们目前掌握的知识来看,只能用继承的方式了。
继承也有两种,一种是继承一个新类,另一种是继承到 MonoBehaviourSimplify 里。
笔者选择后者,这样我们的脚本只要继承 MonoBehaviourSimplify 就会获得 API 简化和消息功能了,一举多得,而且很方便。
集成后的代码,也就是第十三个示例的代码如下:
using System; using System.Collections.Generic; namespace QFramework { public abstract partial class MonoBehaviourSimplify { Dictionary<string, Action<object>> mMsgRegisterRecorder = new Dictionary<string, Action<object>>(); protected void RegisterMsg(string msgName, Action<object> onMsgReceived) { MsgDispatcher.Register(msgName, onMsgReceived); mMsgRegisterRecorder.Add(msgName, onMsgReceived); } private void OnDestroy() { OnBeforeDestroy(); foreach (var keyValuePair in mMsgRegisterRecorder) { MsgDispatcher.UnRegister(keyValuePair.Key,keyValuePair.Value); } mMsgRegisterRecorder.Clear(); } protected abstract void OnBeforeDestroy(); } public class B : MonoBehaviourSimplify { private void Awake() { RegisterMsg("Do", DoSomething); RegisterMsg("DO1", _ => { }); RegisterMsg("DO2", _ => { }); RegisterMsg("DO3", _ => { }); } void DoSomething(object data) { // do something } protected override void OnBeforeDestroy() { } } }在以上代码里,笔者把 MonoBehaviourSimplify 添加了 abstract 关键字,这样用户在使用 MonoBehaviourSimplify 的时候就不能自己创建出来实例了。
而又添加了如下抽象方法:
protected abstract void OnBeforeDestroy();做这步的目的呢,是为了提醒子类不要覆写了 OnDestroy。提醒是怎么做到的呢。