3/8/13

Advanced C# messenger using Enum

Made a little modification to Advanced C# messenger by Ilya Suzdalnitski
basically just changed it to use an enum rather than a string for messages.

This allows you to set up messages in the Message enum, rather than trying to keep track of a bunch of strings. You can also add summaries to the messages to help remember what they are for.

I am sure there is a better way to share this ... but ahhwell







/*
 * 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;

public enum Message
{
/// <summary>
/// This is the default message
/// </summary>
none = 0,
/// <summary>
/// This is a message that does stuff
/// </summary>
messageOne = 1
}

static internal class Messenger
{
#region Internal variables

//Disable the unused variable warning
#pragma warning disable 0414
//Ensures that the MessengerHelper will be created automatically upon start of the game.
static private MessengerHelper messengerHelper = (new GameObject ("MessengerHelper")).AddComponent< MessengerHelper > ();
#pragma warning restore 0414

static public Dictionary<Message, Delegate> eventTable = new Dictionary<Message, Delegate> ();

//Message handlers that should never be removed, regardless of calling Cleanup
static public List< Message > permanentMessages = new List< Message > ();
#endregion
#region Helper methods
//Marks a certain message as permanent.
static public void MarkAsPermanent (Message eventType)
{
#if LOG_ALL_MESSAGES
Debug.Log("Messenger MarkAsPermanent \t\"" + eventType + "\"");
#endif

permanentMessages.Add (eventType);
}

static public void Cleanup ()
{
#if LOG_ALL_MESSAGES
Debug.Log("MESSENGER Cleanup. Make sure that none of necessary listeners are removed.");
#endif

List< Message > messagesToRemove = new List<Message> ();

foreach (KeyValuePair<Message, Delegate> pair in eventTable) {
bool wasFound = false;

foreach (Message message in permanentMessages) {
if (pair.Key == message) {
wasFound = true;
break;
}
}

if (!wasFound)
messagesToRemove.Add (pair.Key);
}

foreach (Message message in messagesToRemove) {
eventTable.Remove (message);
}
}

static public void PrintEventTable ()
{
Debug.Log ("\t\t\t=== MESSENGER PrintEventTable ===");

foreach (KeyValuePair<Message, 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
static public void OnListenerAdding (Message 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));
}
}

static public void OnListenerRemoving (Message 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));
}
}

static public void OnListenerRemoved (Message eventType)
{
if (eventTable [eventType] == null) {
eventTable.Remove (eventType);
}
}

static public void OnBroadcasting (Message 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
}

static public BroadcastException CreateBroadcastSignatureException (Message 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 AddListener
//No parameters
static public void AddListener (Message eventType, Callback handler)
{
OnListenerAdding (eventType, handler);
eventTable [eventType] = (Callback)eventTable [eventType] + handler;
}

//Single parameter
static public void AddListener<T> (Message eventType, Callback<T> handler)
{
OnListenerAdding (eventType, handler);
eventTable [eventType] = (Callback<T>)eventTable [eventType] + handler;
}

//Two parameters
static public void AddListener<T, U> (Message eventType, Callback<T, U> handler)
{
OnListenerAdding (eventType, handler);
eventTable [eventType] = (Callback<T, U>)eventTable [eventType] + handler;
}

//Three parameters
static public void AddListener<T, U, V> (Message eventType, Callback<T, U, V> handler)
{
OnListenerAdding (eventType, handler);
eventTable [eventType] = (Callback<T, U, V>)eventTable [eventType] + handler;
}
#endregion

#region RemoveListener
//No parameters
static public void RemoveListener (Message eventType, Callback handler)
{
OnListenerRemoving (eventType, handler);   
eventTable [eventType] = (Callback)eventTable [eventType] - handler;
OnListenerRemoved (eventType);
}

//Single parameter
static public void RemoveListener<T> (Message eventType, Callback<T> handler)
{
OnListenerRemoving (eventType, handler);
eventTable [eventType] = (Callback<T>)eventTable [eventType] - handler;
OnListenerRemoved (eventType);
}

//Two parameters
static public void RemoveListener<T, U> (Message eventType, Callback<T, U> handler)
{
OnListenerRemoving (eventType, handler);
eventTable [eventType] = (Callback<T, U>)eventTable [eventType] - handler;
OnListenerRemoved (eventType);
}

//Three parameters
static public void RemoveListener<T, U, V> (Message eventType, Callback<T, U, V> handler)
{
OnListenerRemoving (eventType, handler);
eventTable [eventType] = (Callback<T, U, V>)eventTable [eventType] - handler;
OnListenerRemoved (eventType);
}
#endregion

#region Broadcast
//No parameters
static public void Broadcast (Message 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);
}
}
}

//Single parameter
static public void Broadcast<T> (Message 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);
}
}
}

//Two parameters
static public void Broadcast<T, U> (Message 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);
}
}
}

//Three parameters
static public void Broadcast<T, U, V> (Message 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
}

//This manager will ensure that the messenger's eventTable will be cleaned up upon loading of a new level.
public sealed class MessengerHelper : MonoBehaviour
{
void Awake ()
{
DontDestroyOnLoad (gameObject);
}

//Clean up eventTable every time a new level loads.
public void OnDisable ()
{
Messenger.Cleanup ();
}
}

No comments:

Post a Comment