事件讓型別對外發布通知,任意數量消費者可訂閱處理。發送者無需知曉消費者細節,實現鬆散耦合。
public class LoggerEventArgs : EventArgs {
public readonly string Message;
public readonly int Priority;
public LoggerEventArgs(int p, string m) { Priority = p; Message = m; }
}
public delegate void AddMessageEventHandler(object sender, LoggerEventArgs msg);
public class Logger {
private static Logger _theOnly = new Logger();
public static Logger Singleton => _theOnly;
private Logger() {}
public event AddMessageEventHandler Log;
public void AddMsg(int priority, string msg) {
var handler = Log; // 複製委派參考,確保執行緒安全
if (handler != null)
handler(null, new LoggerEventArgs(priority, msg));
}
}
關鍵模式: 先複製事件委派至區域變數再檢查/呼叫,防止多執行緒環境下檢查後、呼叫前被移除處理常式。
public event AddMessageEventHandler Log; 等同於:
private AddMessageEventHandler _Log;
public event AddMessageEventHandler Log {
add { _Log += value; }
remove { _Log -= value; }
}
class ConsoleLogger {
static ConsoleLogger() => Logger.Singleton.Log += Logger_Log;
private static void Logger_Log(object s, LoggerEventArgs e)
=> Console.Error.WriteLine($"{e.Priority}:\t{e.Message}");
}
class EventLogger {
static EventLogger() => Logger.Singleton.Log += Event_Log;
private static void Event_Log(object s, LoggerEventArgs e)
=> logDest?.WriteEntry(e.Message, EventLogEntryType.Information, e.Priority);
}
Logger 完全不知消費者存在,消費者自行決定處理方式。
Windows Forms 控制項擁有大量事件,若每個事件皆為欄位會浪費記憶體。System.ComponentModel.EventHandlerList 以鍵值對儲存事件委派,僅在訂閱時建立條目:
public class Logger {
private static EventHandlerList Handlers = new EventHandlerList();
public static void AddLogger(string system, AddMessageEventHandler ev)
=> Handlers[system] = ev;
public static void AddMsg(string system, int priority, string msg) {
if (string.IsNullOrEmpty(system)) return;
var handler = Handlers[system] as AddMessageEventHandler;
var args = new LoggerEventArgs(priority, msg);
handler?.Invoke(null, args);
// 空字串鍵接收所有訊息
(Handlers[""] as AddMessageEventHandler)?.Invoke(null, args);
}
}
EventHandlerList 節省記憶體。