Search code examples
c#eventsdelegatesparametersmediator

Passing delegates as parameters for mediator/subscriber pattern


I'm looking for the appropriate and elegant way to create Mediator/Subscriber architecture with typed callbacks.

Let's suppose I have a class with 'events' i.e. delegates:

public class E
{
    public delegate void SomethingHappened (float a, int b);
    public delegate void ProgressFinished (int[] c);
}

Now I want to create a Mediator class that would register callbacks to those delegates and dispatch callbacks with supplied parameters:

public class Mediator
{
    public static void Register ( ???, Action callback)
    {
        // supplied delegate += callback
    }

    public static void Dispatch ( ???, params object[] list)
    {
        // executing supplied delegate with params: delegate(list)
    }
}

So I could use it the following way:

// Class A:
Mediator.Register (E.SomethingHappened, OnSomethingHappened);
private void OnSomethingHappened (float a, int b)
{
    //..........
}

// Class B:
Mediator.Dispatch (E.SomethingHappened, 0.1f, 'qwe');

Now the problem is that I can't pass delegate as parameter to Register or Dispatch. How do I solve this problem?


Solution

  • You should take a different approach: let your senders dispatch messages, and have your mediator dispatch them to different handlers based on their type.

    Using generics, this would be refactored to:

    // handlers should be differentiated by message type
    public class SomethingHappenedMessage
    {
        public float A { get; set; }
        public int B { get; set; }
    }
    
    public class Mediator
    {
        private readonly Dictionary<Type, object> _dict = new Dictionary<Type, object>();
    
        public void Register<Tmessage>(Action<Tmessage> callback)
        {
            _dict[typeof(Tmessage)] = callback;
        }
    
        public void Dispatch<Tmessage>(Tmessage msg)
        {
            var handler = _dict[typeof(Tmessage)] as Action<Tmessage>;
            handler(msg);
        }
    }
    

    Or, you might have multiple handlers for each message type:

    public class Mediator
    {
        readonly Dictionary<Type, List<object>> _handlersByType = new Dictionary<Type, List<object>>();
    
        public void Register<Tmessage>(Action<Tmessage> callback)
        {
            List<object> handlers;
            if (!_handlersByType.TryGetValue(typeof(Tmessage), out handlers))
                _handlersByType[typeof(Tmessage)] = handlers = new List<object>();
    
            handlers.Add(callback);
        }
    
        public void Dispatch<Tmessage>(Tmessage msg)
        {
            List<object> handlers;
            if (!_handlersByType.TryGetValue(typeof(Tmessage), out handlers))
                return;
    
            foreach (Action<Tmessage> handler in handlers)
                handler(msg);
        }
    }