NotificationCenter in C#?

I’ve gotten around to working on my network “message” module (NetworkReceiver), that hooks into Smartfox’s OnExtensionResponse event to throw messages to various other modules that are listening.

I came across NotificationCenter on the wiki, which looks great, however it seems to me a lot of it would be made redundant by using interfaces, which I don’t quite understand but understand enough to know that they make something like AddObserver reduntant, because by implementing an interface (called say, INetworkReceiver), you’re already adding an observer, aren’t you?

Has anyone tried anything like this in c#?

Interfaces don’t implement any logic, they are simply used to tell the compiler that a certain class implements a certain set of methods. It doesn’t do anything beyond that.

If you wanted to only use an interface to define if a class should receive a notification, you would still need some code to find all objects implementing the interface so that the sender would know where to send the notifications to.

Also, this kind of approach is less flexible than the way notifications are implemented. Observers can be registered and un-registered at runtime, there’s no pre-defined name you have to give your observers and they can be used in any context without forcing you to implement an interface. If you’d use notifications extensively, you’d end up with a lot of interfaces or having to bundle notifications without possibly needing it.

I would say notifications should be used when you got individual events you want to distribute thorough your game while interfaces should be used when you need a more complex interaction between different classes.

And here’s an untested conversion (a few things renamed):

using System.Collections;
using UnityEngine;

public class MessageDispatcher : MonoBehaviour 
{
    private static MessageDispatcher instance;

    public MessageDispatcher ()
    {
        if (instance != null)
        {
            return;
        }

        instance = this;
    }

    public static MessageDispatcher Instance
    {
        get
        {
            if (instance == null)
            {
                new MessageDispatcher();
            }

            return instance;
        }
    }

    readonly Hashtable messages = new Hashtable();

    public void AddObserver (GameObject observer, string name) 
    { 
        AddObserver(observer, name, null); 
    }

    public void AddObserver (GameObject observer, string name, GameObject sender) 
    {
        if (string.IsNullOrEmpty(name))
        {
            Debug.Log("Null name specified for notification in AddObserver."); 
            return;
        }

        if (!messages.Contains(name)) 
        {
            Debug.Log("AddObserver");
            messages[name] = new ArrayList();
        }
        
        ArrayList notifyList = (ArrayList) messages[name];

        if (!notifyList.Contains(observer))
        {
            notifyList.Add(observer);
        }
    }

    public void RemoveObserver (GameObject observer, string name) 
    {
        ArrayList notifyList = (ArrayList) messages[name];
        
        if (notifyList != null) 
        {
            if (notifyList.Contains(observer))
            {
                notifyList.Remove(observer);
            }

            if (notifyList.Count == 0)
            {
                messages.Remove(name);
            }

            Debug.Log("RemoveObserver");
        }
    }

    public void PostMessage (GameObject aSender, string aName)
    {
        PostMessage(aSender, aName, null);
    }

    public void PostMessage (GameObject aSender, string aName, GameObject aData)
    {
        PostMessage(new Message(aSender, aName, aData));
    }

    public void PostMessage (Message aMessage) 
    {
        if (string.IsNullOrEmpty(aMessage.name))
        {
            Debug.Log("Null name sent to PostMessage."); 
            return;
        }
        ArrayList notifyList = (ArrayList) messages[aMessage.name];

        if (notifyList != null)
        {
            Debug.Log("Notify list not found in PostMessage."); 
            return;
        }
        
        ArrayList observersToRemove = new ArrayList();

        foreach (GameObject observer in notifyList) 
        {
            if (!observer) 
            { 
                observersToRemove.Add(observer);
            } 
            else 
            {
                observer.SendMessage(aMessage.name, aMessage, SendMessageOptions.DontRequireReceiver);
            }
        }
        
        foreach (GameObject observer in observersToRemove) 
        {
            notifyList.Remove(observer);
        }
    }
}

public class Message 
{
    readonly GameObject sender;
    public string name;
    readonly GameObject data;

    public Message (GameObject aSender,  string aName)
    {
        sender = aSender; 
        name = aName; 
        data = null;
    }

    public Message (GameObject aSender, string aName, GameObject aData)
    {
        sender = aSender; 
        name = aName; 
        data = aData;
    }

    #region Getters

    public GameObject Data
    {
        get { return data; }
    }

    public GameObject Sender
    {
        get { return sender; }
    }

    #endregion
}

I’m trying to get the NotificationCenter working in C# using the above code. Seems to compile fine, but when I call it like this:

MessageDispatcher.Instance().PostMessage(this, "OnBumperCollision");

I get an error saying: “MessageDispatcher.Instance cannot be accessed with an instance reference, qualify it with a type name.”

I’m not sure what that means. Can someone help me make sense of how to call PostMessage inside of MessageDispatcher?

Just a style preference thing but since you’re going to be calling MessageDispatcher all the time why type Instance() every call? It’s only going to be called statically anyway.

Instead, make AddObserver, RemoveObserver and PostMessage static functions.

Then you can just do:

MessageDispatcher.PostMessage(...); // yay

Anyway, I have one in Boo I can post that’s inspired by the above which is closer to C# than Javascript is if that’ll help (just add semicolons and braces and it’s pretty close to the same)

So you’re saying I can go:

public static void AddObserver (GameObject observer, string name, GameObject sender)
...

And then it will work? I’m moving over to C# from JS and I’m not familiar with the use of static functions and classes as of yet, hence my struggle. Why does calling Instance() result in an error.

Please post the Boo code, perhaps it will help.

Yeah you can do two things:

  1. make an static and non-static version of the function. The static function just calls the non-static one:
public static void AddObserver(...)
{
    instance.AddObserver_(...); // may have to have a slightly diff name not sure in C#
}
  1. Just write it as a static using the instance variable to access any instance members:
public static void AddObserver(...)
{
    foreach obs in instance.mObservers {...}

I do #1 pretty exclusively. Note you still need to create the ‘instance’ variable as shown in the original code.

Here’s my Messenger class. It’s not an exact copy but may give you some ideas.

The main thing is I don’t use a notification class and the other is that on sending out events, I send them to the GameObect, not the component itself. I was running into issues where if two components wanted the same event, they’d get it twice since any event sent to a game object goes t o all its components. This insures each script wanting the event only gets it once. (You still need all components in your listeners list otherwise if one wants to stop receiving them you won’t know if you should remove it or not).

If you have any other questions, feel free to ask:

import UnityEngine

class Messenger(MonoBehaviour): 
	
	private static instance as Messenger
	private mListeners = {}

	def Awake():
		instance = self
		
	static def Accept(listener, name as string):
		instance.Accept_(listener, name)
	
	private def Accept_(listener, name as string):
		Debug.Log("Messenger::Accept(${name})")
		if not mListeners.Contains(name):
			mListeners[name] = []
		(mListeners[name] as List).Add(listener)
		
	static def Ignore(listener, name as string):
		instance.Ignore_(listener, name)
	
	private def Ignore_(listener, name as string):
		Debug.Log("Messenger::Ignore(${name})")
		if mListeners.Contains(name):
			listeners = mListeners[name] as List
			listeners.Remove(listener)
	
	static def IgnoreAll(listener as Object):
		instance.IgnoreAll_(listener)
		
	private def IgnoreAll_(listener as Object):
		Debug.Log("Messenger::IgnoreAll(${listener.name})")
		for listen_entry in mListeners:
			(listen_entry.Value as List).Remove(listener)

	static def Send(name as string):
		Send(name, null)
		
	static def Send(name as string, params):
		instance.Send_(name, params)
		
	def Send_(name as string, params):
		Debug.Log("Messenger::Send(${name})")
		if not mListeners.Contains(name):
			return
		notified_objects = []
		for listener as Object in mListeners[name]:
			# Get the object's base gameObject
			# So we only need to notify this object a single time
			obj as GameObject = null
			if listener isa GameObject:
				obj = listener as GameObject
			elif listener isa Component:
				obj = (listener as Component).gameObject
			else:
				# Only for debugging -- i can't see this ever happening!
				Debug.Log("${listener} is not a valid receiver")
				continue
			# Have we notified this object yet?
			if notified_objects.Contains(obj):
				continue
			else:
				# method name = On<event-name>
				function = "On${name}"
				# Add to the list for next round
				notified_objects.Add(obj)
				# We can notify this object now
				obj.SendMessage(
					function, 
					params, 
					SendMessageOptions.DontRequireReceiver)

i think i got this working, if anyone cares:

using System.Collections;
using UnityEngine;

public class MessageDispatcher : MonoBehaviour
{
    private static MessageDispatcher instance;

    public MessageDispatcher ()
    {
        if (instance != null)
        {
            return;
        }

        instance = this;
    }

    public static MessageDispatcher Instance
    {
        get
        {
            if (instance == null)
            {
                new MessageDispatcher();
            }

            return instance;
        }
    }

    readonly Hashtable messages = new Hashtable();

    public void AddObserver (GameObject observer, string name)
    {
        AddObserver(observer, name, null);
    }

    public static void AddObserver_ (GameObject observer, string name)
    {
        instance.AddObserver(observer, name, null);
    }

    public void AddObserver (GameObject observer, string name, GameObject sender)
    {
        if (name == null)
        {
            Debug.Log("Null name specified for notification in AddObserver.");
            return;
        }

        if (!messages.Contains(name))
        {
            Debug.Log("AddObserver");
            messages[name] = new ArrayList();
        }
       
        ArrayList notifyList = (ArrayList) messages[name];

        if (!notifyList.Contains(observer))
        {
            notifyList.Add(observer);
        }
    }

    public static void AddObserver_ (GameObject observer, string name, GameObject sender)
    {
        if (name == null)
        {
            Debug.Log("Null name specified for notification in AddObserver.");
            return;
        }

        if (!instance.messages.Contains(name))
        {
            Debug.Log("AddObserver");
            instance.messages[name] = new ArrayList();
        }
       
        ArrayList notifyList = (ArrayList) instance.messages[name];

        if (!notifyList.Contains(observer))
        {
            notifyList.Add(observer);
        }
    }

    public void RemoveObserver (GameObject observer, string name)
    {
        ArrayList notifyList = (ArrayList) messages[name];
       
        if (notifyList != null)
        {
            if (notifyList.Contains(observer))
            {
                notifyList.Remove(observer);
            }

            if (notifyList.Count == 0)
            {
                messages.Remove(name);
            }

            Debug.Log("RemoveObserver");
        }
    }

    public static void RemoveObserver_ (GameObject observer, string name)
    {
        ArrayList notifyList = (ArrayList) instance.messages[name];
       
        if (notifyList != null)
        {
            if (notifyList.Contains(observer))
            {
                notifyList.Remove(observer);
            }

            if (notifyList.Count == 0)
            {
                instance.messages.Remove(name);
            }

            Debug.Log("RemoveObserver");
        }
    }

    public void PostMessage (GameObject aSender, string aName)
    {
        PostMessage(aSender, aName, null);
    }

    public static void PostMessage_ (GameObject aSender, string aName)
    {
        instance.PostMessage(aSender, aName, null);
    }

    public void PostMessage (GameObject aSender, string aName, GameObject aData)
    {
        PostMessage(new Message(aSender, aName, aData));
    }

    public static void PostMessage_ (GameObject aSender, string aName, GameObject aData)
    {
        instance.PostMessage(new Message(aSender, aName, aData));
    }

    public void PostMessage (Message aMessage)
    {
        if (aMessage.name == null )
        {
            Debug.Log("Null name sent to PostMessage.");
            return;
        }
        ArrayList notifyList = (ArrayList) messages[aMessage.name];

        if (notifyList == null)
        {
            Debug.Log("Notify list not found in PostMessage.");
            return;
        }
       
        ArrayList observersToRemove = new ArrayList();

        foreach (GameObject observer in notifyList)
        {
            if (!observer)
            {
                observersToRemove.Add(observer);
            }
            else
            {
                observer.SendMessage(aMessage.name, aMessage, SendMessageOptions.DontRequireReceiver);
            }
        }
       
        foreach (GameObject observer in observersToRemove)
        {
            notifyList.Remove(observer);
        }
    }

    public static void PostMessage_ (Message aMessage)
    {
        if (aMessage.name == null )
        {
            Debug.Log("Null name sent to PostMessage.");
            return;
        }
        ArrayList notifyList = (ArrayList) instance.messages[aMessage.name];

        if (notifyList == null)
        {
            Debug.Log("Notify list not found in PostMessage.");
            return;
        }
       
        ArrayList observersToRemove = new ArrayList();

        foreach (GameObject observer in notifyList)
        {
            if (!observer)
            {
                observersToRemove.Add(observer);
            }
            else
            {
                observer.SendMessage(aMessage.name, aMessage, SendMessageOptions.DontRequireReceiver);
            }
        }
       
        foreach (GameObject observer in observersToRemove)
        {
            notifyList.Remove(observer);
        }
    } 
}

public class Message
{
    public readonly GameObject sender;
    public string name;
    public readonly GameObject data;

    public Message (GameObject aSender,  string aName)
    {
        sender = aSender;
        name = aName;
        data = null;
    }

    public Message (GameObject aSender, string aName, GameObject aData)
    {
        sender = aSender;
        name = aName;
        data = aData;
    }

    #region Getters

    public GameObject Data
    {
        get { return data; }
    }

    public GameObject Sender
    {
        get { return sender; }
    }

    #endregion
}

pretty messy-looking, but it seems to work. you can just call like this:

MessageDispatcher.AddObserver_(this.gameObject, "OnBumperCollision");

there was also a slight error in the logic of duke’s conversion that lead to some pretty loopy behavior that i fixed. i know that underscore almost certainly isn’t acceptable convention but it was the fastest way to get it working. is there a standard naming convention for a situation like this? i’ll change it if there is.

hmm, it seems to be working fine except for this one time i opened up another project and then came back to this only to have it crash on playing in the editor. it then crashed every time i reopened unity iphone and pressed play in the editor. i commented out parts of the code relating to the messages, and eventually got it to not crash, but then on uncommenting nothing crashed again, even once all the code was back the exact same way it was (i’m sure of this, checked it over several times.) i haven’t been able to reproduce the problem. anyone have any idea what’s up with that? just an weird intermittent editor-related bug? some compilation order quirk i need to worry about?

okay, so i gather the crashes are happening because the class is using the constructor. commenting out the whole MessageDispatcher script, saving the script file, uncommenting, and saving again “fixes” it for the time being, but obviously that’s not acceptable for much longer. what i’m not clear on is how to change it so that it uses Awake instead of the constructor. i tried what i thought would be the straightforward way of doing it, and i can’t get it to work. it would be nice if the script reference had an example instead of just saying “do this instead” and i can’t find any other explicit example of how to fix this on the forums.

i’m finding this extremely useful otherwise, much better than the other c# message/event centers i tried because it works in .NET 1.1, the way the message methods are implemented is tidy and makes intuitive sense, i don’t have to make separate event/message scripts, and i can grab pretty much any information from any GameObject as i need to. if we get this one issue sorted out it definitely deserves to go on the wiki.

hmmm, but almost all the unity c# singleton examples i can find work the same way, declaring “instance = this” in the constructor. i’m really stuck here. could anyone else at least give it a try and see if they experience the same problems, so i know it’s not just a problem on my end (i have had problems with totally random unity crashes before, but not recently)?

Hey Guys,
can any one help me to send notification to user in unity android using C# .(its urgent)