提出问题?
我们在unity里如何可以快速的注册委托处理,现实消息监听机制呢?
解决设想
为了可以快速的注册于监听,我希望可以直接在全局中注册/移除/执行 委托处理,全局可以快速的调用,并且只需要在发送消息的对象身上执行一个发送,在需要接收的消息的对象身上注册一个接收就可以了,其中存在一些不可预见的问题,接收函数到底需要接收的是什么参数?发送函数到底发送的是什么参数?如何可以让这两个函数对应上呢?
在注册上的思路是声明 delegate(object) 委托处理接收的参数为object,这样后期不管什么类型可以直接注册,在接收函数中进行对object进行数据匹配,判断object类型,可以实现发送与接收的全局话。
那么又没有什么优化方案呢?可以省去对类型的判断,或者是注册的时候就已经知道了类型呢?带着这个思考继续完善和调整 !
既然我不知道数据类型,那就将委托处理的事件参数变成泛型 委托的事件就变成了delegate<T>(T) 这样就可以实现委托处理的注册时候函数的参数是泛型,那么问题在于我怎么知道我注册的函数于我发送的消息是对应的呢?原本是思考使用 Dictionary<string,delegate> 在对于泛型的支持上,Dictionary本身的Map形式就是{KEY,VALUE(T)} 这样就导致注册进去的都是同一个类型的函数。那么Hashtable和ArrayList 可以注册不同类型的{KEY,VALUE{OBJECT}},那么采用这两货貌似可以解决问题。果断采用了Hashtable。如此一来,在注册过程中就进行内容添加就行了。但是为了执行效率,我们的KEY可以直接于object的类型进行【雪薇】的关联一下让执行更精准,那就是判断一下T的类型直接于key进行关联。这样做的效果就是注册的时候可以方便的找到我需要注册的类型是否已经注册,发送的时候直接找到对应的key进行调用。
代码实现
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 消息机制 /// /// ArrayList alc = new ArrayList(); //保存任意数据类型 /// alc.Add(e); /// </summary> /// public class AppMessage : SingleInstance<AppMessage> { public delegate void MsgEvent<T>(T value); private Hashtable CacheEvent = new Hashtable(); /// <summary> /// 注册消息 /// 提醒:带小数点数字不加 f 默认为double /// </summary> /// <typeparam name="T"></typeparam> /// <param name="name"></param> /// <param name="e"></param> public void On<T>(string name, MsgEvent<T> e) { string key = GetKey<T>(name); if (CacheEvent.ContainsKey(key)) { if (CacheEvent[key] is MsgEvent<T>) { MsgEvent<T> row = (MsgEvent<T>)CacheEvent[key]; if (CheckEvent<T>(row, e)) { Debug.Log("方法已经存在,无需重复添加!"); } else { row += e; CacheEvent[key] = row; } } Debug.LogError("添加监听事件错误----->【" + name + "】【方法的数据类型不匹配】"); return; } CacheEvent.Add(key, e); } /// <summary> /// 移除消息 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="name"></param> /// <param name="e"></param> public void Off<T>(string name, MsgEvent<T> e) { string key = GetKey<T>(name); if (!CacheEvent.ContainsKey(key)) return; if (CacheEvent[key] is MsgEvent<T>) { MsgEvent<T> row = (MsgEvent<T>)CacheEvent[key]; Delegate[] delegates = row.GetInvocationList(); if (delegates.Length < 2) { CacheEvent.Remove(key); } else { row -= e; CacheEvent[key] = row; } //Debug.Log("移除事件【" + key+"】"); } } /// <summary> /// 移除分类 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="name"></param> public void Off<T>(string name) { string key = GetKey<T>(name); if (!CacheEvent.ContainsKey(key)) return; CacheEvent.Remove(key); } /// <summary> /// 清空 /// </summary> public void Clear() { CacheEvent.Clear(); } /// <summary> /// 发送消息 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="name"></param> /// <param name="value"></param> /// <returns>true 或 false</returns> public bool Emit<T>(string name, T value) { string key = GetKey<T>(name); if (!CacheEvent.ContainsKey(key)) return false; if (CacheEvent[key] is MsgEvent<T>) { MsgEvent<T> row = (MsgEvent<T>)CacheEvent[key]; row.Invoke(value); return true; } return false; } private string GetKey<T>(string name) { string t = typeof(T).ToString().Replace(".", "").Replace("`", "").ToLower(); return name + t; } private bool CheckEvent<T>(MsgEvent<T> row, MsgEvent<T> e) { if (row == null) return false; Delegate[] delegates = row.GetInvocationList(); foreach (Delegate item in delegates) { if (item.Equals(e)) return true; } return false; } }
这样注册使用的方法
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Test : MonoBehaviour { void Start() { AppMessage.Instance.On<int>("test", setValue0);//注册消息 AppMessage.Instance.Off<int>("test", setValue0); //移除注册 AppMessage.Instance.Emit<int>("test", 15); //发送消息 } void OnDestroy() { } void setValue0(int v) { Debug.Log("set value 0 ======>" + v); } }
根据泛型的类型不同可以直接区分出调用的函数类型 SingleInstance<AppMessage> 为创建单例对象,以后全局使用。