解耦合--對委託的進一步封裝,使委託更容易使用
阿新 • • 發佈:2020-12-30
今天在學習過程中使用了一段可以複用的解耦合程式碼,使用字典+委託+監聽+廣播的方式實現,特別記錄在下面:
一共通過三個.cs檔案實現,程式碼內容如下:
public delegate void CallBack(); public delegate void CallBack<T>(T arg); public delegate void CallBack<T, X>(T arg1, X arg2); public delegate void CallBack<T, X, Y>(T arg1, X arg2, Y arg3); public delegatevoid CallBack<T, X, Y, Z>(T arg1, X arg2, Y arg3, Z arg4); public delegate void CallBack<T, X, Y, Z, W>(T arg1, X arg2, Y arg3, Z arg4, W arg5);
這段程式碼定義了所有的委託型別,使用泛型指定委託引數,提供了0-5個引數的無返回值委託以便註冊。
public enum EventDefine { }
定義一個列舉型別用於儲存事件的名稱
using System; using System.Collections; usingSystem.Collections.Generic; using UnityEngine; public class EventCenter { private static Dictionary<EventDefine, Delegate> m_EventTable = new Dictionary<EventDefine, Delegate>(); private static void OnListenerAdding(EventDefine eventType, Delegate callBack) { if (!m_EventTable.ContainsKey(eventType)) { m_EventTable.Add(eventType,null); } Delegate d = m_EventTable[eventType]; if (d != null && d.GetType() != callBack.GetType()) { throw new Exception(string.Format("嘗試為事件{0}新增不同型別的委託,當前事件所對應的委託是{1},要新增的委託型別為{2}", eventType, d.GetType(), callBack.GetType())); } } private static void OnListenerRemoving(EventDefine eventType, Delegate callBack) { if (m_EventTable.ContainsKey(eventType)) { Delegate d = m_EventTable[eventType]; if (d == null) { throw new Exception(string.Format("移除監聽錯誤:事件{0}沒有對應的委託", eventType)); } else if (d.GetType() != callBack.GetType()) { throw new Exception(string.Format("移除監聽錯誤:嘗試為事件{0}移除不同型別的委託,當前委託型別為{1},要移除的委託型別為{2}", eventType, d.GetType(), callBack.GetType())); } } else { throw new Exception(string.Format("移除監聽錯誤:沒有事件碼{0}", eventType)); } } private static void OnListenerRemoved(EventDefine eventType) { if (m_EventTable[eventType] == null) { m_EventTable.Remove(eventType); } } //no parameters public static void AddListener(EventDefine eventType, CallBack callBack) { OnListenerAdding(eventType, callBack); m_EventTable[eventType] = (CallBack)m_EventTable[eventType] + callBack; } //Single parameters public static void AddListener<T>(EventDefine eventType, CallBack<T> callBack) { OnListenerAdding(eventType, callBack); m_EventTable[eventType] = (CallBack<T>)m_EventTable[eventType] + callBack; } //two parameters public static void AddListener<T, X>(EventDefine eventType, CallBack<T, X> callBack) { OnListenerAdding(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X>)m_EventTable[eventType] + callBack; } //three parameters public static void AddListener<T, X, Y>(EventDefine eventType, CallBack<T, X, Y> callBack) { OnListenerAdding(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X, Y>)m_EventTable[eventType] + callBack; } //four parameters public static void AddListener<T, X, Y, Z>(EventDefine eventType, CallBack<T, X, Y, Z> callBack) { OnListenerAdding(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X, Y, Z>)m_EventTable[eventType] + callBack; } //five parameters public static void AddListener<T, X, Y, Z, W>(EventDefine eventType, CallBack<T, X, Y, Z, W> callBack) { OnListenerAdding(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X, Y, Z, W>)m_EventTable[eventType] + callBack; } //no parameters public static void RemoveListener(EventDefine eventType, CallBack callBack) { OnListenerRemoving(eventType, callBack); m_EventTable[eventType] = (CallBack)m_EventTable[eventType] - callBack; OnListenerRemoved(eventType); } //single parameters public static void RemoveListener<T>(EventDefine eventType, CallBack<T> callBack) { OnListenerRemoving(eventType, callBack); m_EventTable[eventType] = (CallBack<T>)m_EventTable[eventType] - callBack; OnListenerRemoved(eventType); } //two parameters public static void RemoveListener<T, X>(EventDefine eventType, CallBack<T, X> callBack) { OnListenerRemoving(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X>)m_EventTable[eventType] - callBack; OnListenerRemoved(eventType); } //three parameters public static void RemoveListener<T, X, Y>(EventDefine eventType, CallBack<T, X, Y> callBack) { OnListenerRemoving(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X, Y>)m_EventTable[eventType] - callBack; OnListenerRemoved(eventType); } //four parameters public static void RemoveListener<T, X, Y, Z>(EventDefine eventType, CallBack<T, X, Y, Z> callBack) { OnListenerRemoving(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X, Y, Z>)m_EventTable[eventType] - callBack; OnListenerRemoved(eventType); } //five parameters public static void RemoveListener<T, X, Y, Z, W>(EventDefine eventType, CallBack<T, X, Y, Z, W> callBack) { OnListenerRemoving(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X, Y, Z, W>)m_EventTable[eventType] - callBack; OnListenerRemoved(eventType); } //no parameters public static void Broadcast(EventDefine eventType) { Delegate d; if (m_EventTable.TryGetValue(eventType, out d)) { CallBack callBack = d as CallBack; if (callBack != null) { callBack(); } else { throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具有不同的型別", eventType)); } } } //single parameters public static void Broadcast<T>(EventDefine eventType, T arg) { Delegate d; if (m_EventTable.TryGetValue(eventType, out d)) { CallBack<T> callBack = d as CallBack<T>; if (callBack != null) { callBack(arg); } else { throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具有不同的型別", eventType)); } } } //two parameters public static void Broadcast<T, X>(EventDefine eventType, T arg1, X arg2) { Delegate d; if (m_EventTable.TryGetValue(eventType, out d)) { CallBack<T, X> callBack = d as CallBack<T, X>; if (callBack != null) { callBack(arg1, arg2); } else { throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具有不同的型別", eventType)); } } } //three parameters public static void Broadcast<T, X, Y>(EventDefine eventType, T arg1, X arg2, Y arg3) { Delegate d; if (m_EventTable.TryGetValue(eventType, out d)) { CallBack<T, X, Y> callBack = d as CallBack<T, X, Y>; if (callBack != null) { callBack(arg1, arg2, arg3); } else { throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具有不同的型別", eventType)); } } } //four parameters public static void Broadcast<T, X, Y, Z>(EventDefine eventType, T arg1, X arg2, Y arg3, Z arg4) { Delegate d; if (m_EventTable.TryGetValue(eventType, out d)) { CallBack<T, X, Y, Z> callBack = d as CallBack<T, X, Y, Z>; if (callBack != null) { callBack(arg1, arg2, arg3, arg4); } else { throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具有不同的型別", eventType)); } } } //five parameters public static void Broadcast<T, X, Y, Z, W>(EventDefine eventType, T arg1, X arg2, Y arg3, Z arg4, W arg5) { Delegate d; if (m_EventTable.TryGetValue(eventType, out d)) { CallBack<T, X, Y, Z, W> callBack = d as CallBack<T, X, Y, Z, W>; if (callBack != null) { callBack(arg1, arg2, arg3, arg4, arg5); } else { throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具有不同的型別", eventType)); } } } }
核心程式碼,下面對這段程式碼分部分解析:
private static Dictionary<EventDefine, Delegate> m_EventTable = new Dictionary<EventDefine, Delegate>();
首先定義了一個字典用於儲存事件和委託之間的對應關係
private static void OnListenerAdding(EventDefine eventType, Delegate callBack) { if (!m_EventTable.ContainsKey(eventType)) { m_EventTable.Add(eventType, null); } Delegate d = m_EventTable[eventType]; if (d != null && d.GetType() != callBack.GetType()) { throw new Exception(string.Format("嘗試為事件{0}新增不同型別的委託,當前事件所對應的委託是{1},要新增的委託型別為{2}", eventType, d.GetType(), callBack.GetType())); } } private static void OnListenerRemoving(EventDefine eventType, Delegate callBack) { if (m_EventTable.ContainsKey(eventType)) { Delegate d = m_EventTable[eventType]; if (d == null) { throw new Exception(string.Format("移除監聽錯誤:事件{0}沒有對應的委託", eventType)); } else if (d.GetType() != callBack.GetType()) { throw new Exception(string.Format("移除監聽錯誤:嘗試為事件{0}移除不同型別的委託,當前委託型別為{1},要移除的委託型別為{2}", eventType, d.GetType(), callBack.GetType())); } } else { throw new Exception(string.Format("移除監聽錯誤:沒有事件碼{0}", eventType)); } } private static void OnListenerRemoved(EventDefine eventType) { if (m_EventTable[eventType] == null) { m_EventTable.Remove(eventType); } }
接著這裡是當進入監聽、移除監聽中和移除監聽後呼叫的方法,用於檢測字典中是否有要操作的事件碼和對應的委託等,如果在新增或移除的過程中有任何問題會丟擲異常。
//no parameters public static void AddListener(EventDefine eventType, CallBack callBack) { OnListenerAdding(eventType, callBack); m_EventTable[eventType] = (CallBack)m_EventTable[eventType] + callBack; } //Single parameters public static void AddListener<T>(EventDefine eventType, CallBack<T> callBack) { OnListenerAdding(eventType, callBack); m_EventTable[eventType] = (CallBack<T>)m_EventTable[eventType] + callBack; } //two parameters public static void AddListener<T, X>(EventDefine eventType, CallBack<T, X> callBack) { OnListenerAdding(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X>)m_EventTable[eventType] + callBack; } //three parameters public static void AddListener<T, X, Y>(EventDefine eventType, CallBack<T, X, Y> callBack) { OnListenerAdding(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X, Y>)m_EventTable[eventType] + callBack; } //four parameters public static void AddListener<T, X, Y, Z>(EventDefine eventType, CallBack<T, X, Y, Z> callBack) { OnListenerAdding(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X, Y, Z>)m_EventTable[eventType] + callBack; } //five parameters public static void AddListener<T, X, Y, Z, W>(EventDefine eventType, CallBack<T, X, Y, Z, W> callBack) { OnListenerAdding(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X, Y, Z, W>)m_EventTable[eventType] + callBack; } //no parameters public static void RemoveListener(EventDefine eventType, CallBack callBack) { OnListenerRemoving(eventType, callBack); m_EventTable[eventType] = (CallBack)m_EventTable[eventType] - callBack; OnListenerRemoved(eventType); } //single parameters public static void RemoveListener<T>(EventDefine eventType, CallBack<T> callBack) { OnListenerRemoving(eventType, callBack); m_EventTable[eventType] = (CallBack<T>)m_EventTable[eventType] - callBack; OnListenerRemoved(eventType); } //two parameters public static void RemoveListener<T, X>(EventDefine eventType, CallBack<T, X> callBack) { OnListenerRemoving(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X>)m_EventTable[eventType] - callBack; OnListenerRemoved(eventType); } //three parameters public static void RemoveListener<T, X, Y>(EventDefine eventType, CallBack<T, X, Y> callBack) { OnListenerRemoving(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X, Y>)m_EventTable[eventType] - callBack; OnListenerRemoved(eventType); } //four parameters public static void RemoveListener<T, X, Y, Z>(EventDefine eventType, CallBack<T, X, Y, Z> callBack) { OnListenerRemoving(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X, Y, Z>)m_EventTable[eventType] - callBack; OnListenerRemoved(eventType); } //five parameters public static void RemoveListener<T, X, Y, Z, W>(EventDefine eventType, CallBack<T, X, Y, Z, W> callBack) { OnListenerRemoving(eventType, callBack); m_EventTable[eventType] = (CallBack<T, X, Y, Z, W>)m_EventTable[eventType] - callBack; OnListenerRemoved(eventType); }
然後是各種過載的新增監聽和移除監聽的方法,在新增或移除時會呼叫剛才的檢測程式碼進行檢測,確認可以新增監聽(將事件碼和對應委託加入字典)或者移除監聽(將事件碼和對應委託移除出字典)。
//no parameters public static void Broadcast(EventDefine eventType) { Delegate d; if (m_EventTable.TryGetValue(eventType, out d)) { CallBack callBack = d as CallBack; if (callBack != null) { callBack(); } else { throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具有不同的型別", eventType)); } } } //single parameters public static void Broadcast<T>(EventDefine eventType, T arg) { Delegate d; if (m_EventTable.TryGetValue(eventType, out d)) { CallBack<T> callBack = d as CallBack<T>; if (callBack != null) { callBack(arg); } else { throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具有不同的型別", eventType)); } } } //two parameters public static void Broadcast<T, X>(EventDefine eventType, T arg1, X arg2) { Delegate d; if (m_EventTable.TryGetValue(eventType, out d)) { CallBack<T, X> callBack = d as CallBack<T, X>; if (callBack != null) { callBack(arg1, arg2); } else { throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具有不同的型別", eventType)); } } } //three parameters public static void Broadcast<T, X, Y>(EventDefine eventType, T arg1, X arg2, Y arg3) { Delegate d; if (m_EventTable.TryGetValue(eventType, out d)) { CallBack<T, X, Y> callBack = d as CallBack<T, X, Y>; if (callBack != null) { callBack(arg1, arg2, arg3); } else { throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具有不同的型別", eventType)); } } } //four parameters public static void Broadcast<T, X, Y, Z>(EventDefine eventType, T arg1, X arg2, Y arg3, Z arg4) { Delegate d; if (m_EventTable.TryGetValue(eventType, out d)) { CallBack<T, X, Y, Z> callBack = d as CallBack<T, X, Y, Z>; if (callBack != null) { callBack(arg1, arg2, arg3, arg4); } else { throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具有不同的型別", eventType)); } } } //five parameters public static void Broadcast<T, X, Y, Z, W>(EventDefine eventType, T arg1, X arg2, Y arg3, Z arg4, W arg5) { Delegate d; if (m_EventTable.TryGetValue(eventType, out d)) { CallBack<T, X, Y, Z, W> callBack = d as CallBack<T, X, Y, Z, W>; if (callBack != null) { callBack(arg1, arg2, arg3, arg4, arg5); } else { throw new Exception(string.Format("廣播事件錯誤:事件{0}對應委託具有不同的型別", eventType)); } } }
最後是各種廣播的過載方法。呼叫廣播方法,傳入對應的引數和事件碼,將在字典中查詢事件碼對應的委託,如果委託存在,將使用傳入的引數呼叫委託,否則會丟擲異常。
總結:基本的實現思路是事先定義好委託型別和事件碼,使用字典儲存委託和事件碼,只需要呼叫broadcast方法給出事件碼和引數就可以根據事件碼查詢相應的委託並呼叫。在實際使用中,通過呼叫addListener方法註冊委託,呼叫removeListener方法移除委託,通過呼叫broadcast方法呼叫相應的委託。本質上,還是使用的委託實現解耦合,這裡將委託進一步封裝,實現了更簡易的使用。這裡只實現流量0-5個引數的無返回值的委託,如果有興趣的話可以自行封裝更多引數的委託、帶返回值型別的委託甚至多播委託。