觀察者模式(基於CSharpMessenger擴充套件)
阿新 • • 發佈:2018-12-11
在使用c#中的事件時,經同事推薦看到了這個框架,它是c#訊息傳遞的高階版本,程式碼不多,拜讀之後對程式碼結構進行了重構,感覺更清晰點
/* * Advanced C# messenger by Ilya Suzdalnitski. V1.0 * * Based on Rod Hyde's "CSharpMessenger" and Magnus Wolffelt's "CSharpMessenger Extended". * * Features: * Prevents a MissingReferenceException because of a reference to a destroyed message handler. * Option to log all messages * Extensive error detection, preventing silent bugs * * Usage examples: 1. Messenger.AddListener<GameObject>("prop collected", PropCollected); Messenger.Broadcast<GameObject>("prop collected", prop); 2. Messenger.AddListener<float>("speed changed", SpeedChanged); Messenger.Broadcast<float>("speed changed", 0.5f); * * Messenger cleans up its evenTable automatically upon loading of a new level. * * Don't forget that the messages that should survive the cleanup, should be marked with Messenger.MarkAsPermanent(string) * */ //巨集定不許刪除,為以後測試方便 //#define LOG_ALL_MESSAGES //#define LOG_ADD_LISTENER //#define LOG_BROADCAST_MESSAGE #define REQUIRE_LISTENER using System; using System.Collections.Generic; using UnityEngine; namespace GG { public class Messenger { public static Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>(); //Message handlers that should never be removed, regardless of calling Cleanup public static List<string> permanentMessages = new List<string>(); #region Helper methods //Marks a certain message as permanent. public static void MarkAsPermanent(string eventType) { #if LOG_ALL_MESSAGES Debug.Log("Messenger MarkAsPermanent \t\"" + eventType + "\""); #endif permanentMessages.Add(eventType); } public static void Cleanup() { #if LOG_ALL_MESSAGES Debug.Log("MESSENGER Cleanup. Make sure that none of necessary listeners are removed."); #endif List<string> messagesToRemove = new List<string>(); foreach (KeyValuePair<string, Delegate> pair in eventTable) { bool wasFound = false; foreach (string message in permanentMessages) { if (pair.Key == message) { wasFound = true; break; } } if (!wasFound) messagesToRemove.Add(pair.Key); } foreach (string message in messagesToRemove) { eventTable.Remove(message); } } public static void PrintEventTable() { Debug.Log("\t\t\t=== MESSENGER PrintEventTable ==="); foreach (KeyValuePair<string, Delegate> pair in eventTable) { Debug.Log("\t\t\t" + pair.Key + "\t\t" + pair.Value); } Debug.Log("\n"); } #endregion #region Message logging and exception throwing public static void OnListenerAdding(string eventType, Delegate listenerBeingAdded) { #if LOG_ALL_MESSAGES || LOG_ADD_LISTENER Debug.Log("MESSENGER OnListenerAdding \t\"" + eventType + "\"\t{" + listenerBeingAdded.Target + " -> " + listenerBeingAdded.Method + "}"); #endif if (!eventTable.ContainsKey(eventType)) { eventTable.Add(eventType, null); } Delegate d = eventTable[eventType]; if (d != null && d.GetType() != listenerBeingAdded.GetType()) { throw new ListenerException(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)); } } public static void OnListenerRemoving(string eventType, Delegate listenerBeingRemoved) { #if LOG_ALL_MESSAGES Debug.Log("MESSENGER OnListenerRemoving \t\"" + eventType + "\"\t{" + listenerBeingRemoved.Target + " -> " + listenerBeingRemoved.Method + "}"); #endif if (eventTable.ContainsKey(eventType)) { Delegate d = eventTable[eventType]; if (d == null) { throw new ListenerException(string.Format("Attempting to remove listener with for event type \"{0}\" but current listener is null.", eventType)); } else if (d.GetType() != listenerBeingRemoved.GetType()) { throw new ListenerException(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)); } } else { throw new ListenerException(string.Format("Attempting to remove listener for type \"{0}\" but Messenger doesn't know about this event type.", eventType)); } } public static void OnListenerRemoved(string eventType) { if (eventTable[eventType] == null) { eventTable.Remove(eventType); } } public static void OnBroadcasting(string eventType) { #if REQUIRE_LISTENER if (!eventTable.ContainsKey(eventType)) { throw new BroadcastException(string.Format("Broadcasting message \"{0}\" but no listener found. Try marking the message with Messenger.MarkAsPermanent.", eventType)); } #endif } public static BroadcastException CreateBroadcastSignatureException(string eventType) { return new BroadcastException(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.", eventType)); } public class BroadcastException : Exception { public BroadcastException(string msg) : base(msg) { } } public class ListenerException : Exception { public ListenerException(string msg) : base(msg) { } } #endregion } #region No parameters public class MessengerMgr : Messenger { public static void AddListener(string eventType, Callback handler) { OnListenerAdding(eventType, handler); var temp = (Callback)eventTable[eventType]; temp += handler; eventTable[eventType] = (Callback)eventTable[eventType] + handler; } public static void RemoveListener(string eventType, Callback handler) { OnListenerRemoving(eventType, handler); eventTable[eventType] = (Callback)eventTable[eventType] - handler; OnListenerRemoved(eventType); } public static void Broadcast(string eventType) { #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\""); #endif OnBroadcasting(eventType); Delegate d; if (eventTable.TryGetValue(eventType, out d)) { Callback callback = d as Callback; if (callback != null) { callback(); } else { throw CreateBroadcastSignatureException(eventType); } } } } #endregion #region Single parameters public class MessengerMgr<T> : Messenger { public static void AddListener(string eventType, Callback<T> handler) { OnListenerAdding(eventType, handler); eventTable[eventType] = (Callback<T>)eventTable[eventType] + handler; } public static void RemoveListener(string eventType, Callback<T> handler) { OnListenerRemoving(eventType, handler); eventTable[eventType] = (Callback<T>)eventTable[eventType] - handler; OnListenerRemoved(eventType); } public static void Broadcast(string eventType, T arg1) { #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\""); #endif OnBroadcasting(eventType); Delegate d; if (eventTable.TryGetValue(eventType, out d)) { Callback<T> callback = d as Callback<T>; if (callback != null) { callback(arg1); } else { throw CreateBroadcastSignatureException(eventType); } } } } #endregion #region Two parameters public class MessengerMgr<T, U> : Messenger { public static void AddListener(string eventType, Callback<T, U> handler) { OnListenerAdding(eventType, handler); eventTable[eventType] = (Callback<T, U>)eventTable[eventType] + handler; } public static void RemoveListener(string eventType, Callback<T, U> handler) { OnListenerRemoving(eventType, handler); eventTable[eventType] = (Callback<T, U>)eventTable[eventType] - handler; OnListenerRemoved(eventType); } public static void Broadcast(string eventType, T arg1, U arg2) { #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\""); #endif OnBroadcasting(eventType); Delegate d; if (eventTable.TryGetValue(eventType, out d)) { Callback<T, U> callback = d as Callback<T, U>; if (callback != null) { callback(arg1, arg2); } else { throw CreateBroadcastSignatureException(eventType); } } } } #endregion #region Three parameters public class MessengerMgr<T, U, V> : Messenger { public static void AddListener(string eventType, Callback<T, U, V> handler) { OnListenerAdding(eventType, handler); eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] + handler; } public static void RemoveListener(string eventType, Callback<T, U, V> handler) { OnListenerRemoving(eventType, handler); eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] - handler; OnListenerRemoved(eventType); } public static void Broadcast(string eventType, T arg1, U arg2, V arg3) { #if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE Debug.Log("MESSENGER\t" + System.DateTime.Now.ToString("hh:mm:ss.fff") + "\t\t\tInvoking \t\"" + eventType + "\""); #endif OnBroadcasting(eventType); Delegate d; if (eventTable.TryGetValue(eventType, out d)) { Callback<T, U, V> callback = d as Callback<T, U, V>; if (callback != null) { callback(arg1, arg2, arg3); } else { throw CreateBroadcastSignatureException(eventType); } } } } #endregion }
委託型別定義:
/** * 名稱空間: namespace GG * 功 能: 訊息回撥函式型別 * 類 名: 無 * 建立人:zxh * 建立時間:2018/11/23 13:42:52 * Copyright (c) 2018 gg Corporation. All rights reserved. */ namespace GG { public delegate void Callback(); public delegate void Callback<T>(T arg1); public delegate void Callback<T, U>(T arg1, U arg2); public delegate void Callback<T, U, V>(T arg1, U arg2, V arg3); }
包裝層:
純屬個人編碼習慣,包一層儘量做到使用和實現分離
/** * 名稱空間: namespace GG * 功 能: 訊息包裝層 * 類 名: EventManager * 建立人:zxh * 建立時間:2018/11/23 13:42:52 * Copyright (c) 2018 gg Corporation. All rights reserved. */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using XLua; namespace GG { [LuaCallCSharp] public class EventManager { public static void AddEventListener(string eventName, Callback callback) { MessengerMgr.AddListener(eventName, callback); } public static void AddEventListener<T>(string eventName, Callback<T> callback) { MessengerMgr<T>.AddListener(eventName, callback); } public static void AddEventListener<T, U>(string eventName, Callback<T, U> callback) { MessengerMgr<T, U>.AddListener(eventName, callback); } public static void AddEventListener<T, U, V>(string eventName, Callback<T, U, V> callback) { MessengerMgr<T, U, V>.AddListener(eventName, callback); } public static void RemoVeEventListener(string eventName, Callback callback) { MessengerMgr.RemoveListener(eventName, callback); } public static void RemoVeEventListener<T>(string eventName, Callback<T> callback) { MessengerMgr<T>.RemoveListener(eventName, callback); } public static void RemoVeEventListener<T, U>(string eventName, Callback<T, U> callback) { MessengerMgr<T, U>.RemoveListener(eventName, callback); } public static void RemoVeEventListener<T, U, V>(string eventName, Callback<T, U, V> callback) { MessengerMgr<T, U, V>.RemoveListener(eventName, callback); } public static void DispatchEvent(string eventName) { MessengerMgr.Broadcast(eventName); } public static void DispatchEvent<T>(string eventName, T arg1) { MessengerMgr<T>.Broadcast(eventName, arg1); } public static void DispatchEvent<T, U>(string eventName, T arg1 , U arg2) { MessengerMgr<T, U>.Broadcast(eventName, arg1, arg2); } public static void DispatchEvent<T, U, V>(string eventName, T arg1, U arg2, V arg3) { MessengerMgr<T, U, V>.Broadcast(eventName, arg1, arg2, arg3); } } }
原始碼共有兩個檔案,加上我包裝的一層有三個檔案,程式碼結構很清晰,使用也很簡單,就是一個觀察者模式的實現,相信對c#有了解的開發人員都能看懂,