Tuesday, May 6, 2014

super simple event bus C#

Hi,

today I thought about how to decouple parts of my application. With decoupling I at first thought about a manager which dynamically loads modules and then uses their output to feed a following module... not quite flexible if you more or less "pipe" one part after another and then just chain their results (like in a LINUX-bash command call e.g.: "ps -aux | grep vi" where a manager calls first ps and then grep).

A better approach is to let a global instance store the data when it appears and then push it dependent of the use case to the module which needs it (application #1 lists its errors -> manager pushes it to the log manager -> write to log). This brought me to a big question: Do I need a coordinator if the software parts can talk between each other?

As usual the answer is "it depends". A manager or coordinator is e.g.: OK if you have a main system like notepad++ and you want to load add-on functionality to use another output formatter. Let the modules talk between each other if e.g.: you separate the business logic in independent parts (divide and conquer), but on the same level (not as master-slave).

For my application the communication of the software parts seems to look good. Now I asked myself how to communicate inside of an application (and still as a condition: decoupled). Middleware is over sized for a single application. Message queues or messaging in general seems over sized too. Eventbus was over sized in the first place too, but here google came in the game. I google-d different kinds of event buses and found awesome solutions. Open source and commercial. Big libraries and light-weight solutions. Then I found a very inspiring post of trystan ( http://trystans.blogspot.co.at/2012/01/simplest-eventbus.html / http://trystans.blogspot.co.at/2012/01/simplest-typesafe-eventbus.html ).

I decided to use an own event bus. The following code shows the basic idea (an event bus based on the version of trystan's blog). I decided to build the event bus system more like the .Net's event system using EventHandler and EventArgs and call an event by name.

My code follows below... it is still a basic solution, but it worked for me.

kind regards,
Daniel

BusEventArgs.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EventBus
{
    public class BusEventArgs : EventArgs
    {
        public BusEventArgs(object value = null)
        {
            Value = value;
        }

        public object Value { get; private set; }

        public static BusEventArgs Empty = new BusEventArgs();
    }
}

EventBus.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EventBus
{
    public static class EventBus
    {
        private static Dictionary<string, List<EventHandler<BusEventArgs>>> handlers
            = new Dictionary<string, List<EventHandler<BusEventArgs>>>();

        public static void Publish(string eventName, BusEventArgs e)
        {
            Publish(null, eventName, e);
        }

        public static void Publish(object sender, string eventName, BusEventArgs e)
        {

            foreach (EventHandler<BusEventArgs> handler in handlers[eventName])
            {
                handler(sender, e);
            }
        }

        public static void Subscribe(string eventName, EventHandler<BusEventArgs> handler)
        {
            if (!handlers.ContainsKey(eventName))
            {
                handlers.Add(eventName, new List<EventHandler<BusEventArgs>>());
            }
            handlers[eventName].Add(handler);
        }
    }

}

Tester.cs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace EventBus
{
    class Program
    {
        public static object locker = new object();

        public static bool IsStoped;

        static void Main(string[] args)
        {
            Thread raiser = new Thread(() => Raise());
            Thread listener = new Thread(() => Listen());

            raiser.Start();
            listener.Start();

            Console.ReadLine();
        }

        private static object Listen()
        {
            IsStoped = false;

            EventBus.Subscribe("onRaise", (sender, e) => WriteRaised());

            while (!IsStoped)
            {
                Thread.Sleep(100);
            }
            Console.WriteLine("finished");

            return null;
        }

        private static object WriteRaised()
        {
            Console.WriteLine("raised");
            IsStoped = true;
            return null;
        }

        private static object Raise()
        {
            Thread.Sleep(5000);
            EventBus.Publish(new object(), "onRaise", new BusEventArgs());

            return null;
        }
    }
}

No comments: