2

I'm trying to implement simple Event Bus

I started like this:

public class RegistrationData
{
    public object RegisteredObject { get; set; }
    public Type eventType { get; set; }
    public EventHandler MethodToInvoke;
}
public class EventBus
{
    private static EventBus instance;
    private static readonly object lockObject = new object();
    private static List<RegistrationData> registrationList;

    private EventBus()
    {
    }

    public static EventBus Instance
    {
        get
        {
            lock (lockObject)
            {
                registrationList = new List<RegistrationData>();
                return instance ?? new EventBus();
            }
        }
    }

    public void Subscribe(RegistrationData registrationData)
    {
        if(!registrationList.Contains(registrationData)) registrationList.Add(registrationData);
    }

    public void Unsubscribe(object objectToUnregister, Type eventType)
    {
        foreach(RegistrationData data in registrationList)
            if (data.RegisteredObject == objectToUnregister && data.eventType == eventType) registrationList.Remove(data);
    }

    public void UnregisterAllMessages(object objectToUnregister)
    {
        foreach(RegistrationData data in registrationList)
            if(data.RegisteredObject == objectToUnregister) registrationList.Remove(data);
    }

    public void PublishEvent(object sender, EventArgs eventArgs)
    {
        foreach (RegistrationData data in registrationList)
            if (EventArgs is typeof(data.Type)) data.MethodToInvoke(sender, eventArgs);
    }
}

But I have problem in PublishEvent method. I'm unable to determine type of event argument. And I'm suspicious that all this is pretty wrong.

Can someone point out what I do wrong, how should I implement this? Or how event bus is generally implemented, or some framework which I can use instead of implementing my own and spending time on that.

Vajda
  • 1,795
  • 6
  • 39
  • 49
  • 1
    Hi I did a porting of event bus of guava google's project to .NET. I saved it at github : https://github.com/liorcohenflw/EventBus . – Or Cohen Sep 16 '13 at 10:23

3 Answers3

2

Um, not really sure how your Eventbus should behave. Short of knowing where you are heading it can be useful to look at how other people implemented the problem.

  • There is a down-to-earth event aggregator in the caliburn.micro project
  • I like to use MemBus when I need an event aggregator, partly because I wrote it myself, partly because it covers all my needs in that respect. It is more involved than caliburn's one, but then it has more features
flq
  • 22,247
  • 8
  • 55
  • 77
  • caliburn.micro event aggregator looks pretty nice. I'll probably use it. I'll tomorrow take a look at MemBus to see which one will I use. – Vajda Jul 10 '11 at 20:56
  • MemBus seems to be without any documentation, very unpleasant to start with. Do you know where I can find documentation about MemBus? – Vajda Jul 12 '11 at 11:43
  • You can read my blog entries http://realfiction.net/content/tag/membus as well as an introduction at where the code is stored: https://github.com/flq/MemBus/wiki – flq Jul 12 '11 at 19:03
  • Link to your blog doesn't work, I assume this is the correct link? http://realfiction.net/tag/membus/0 – RenniePet Oct 04 '14 at 16:05
  • Works for me...site was probably down – flq Oct 12 '14 at 09:08
2

I think you should start by defining Event Bus. What do you see as the difference between an Event Bus and the built-in .NET mechanisms for firing and sinking events? What you have so far looks like it implements not much more than the equivalent of .NET events. .NET intrinsically supports event handling so you wouldn't need an event bus if you don't need more than what .NET already provides:

class Program
{
  static void Main(string[] args)
  {
     BusinessObject1 bo = new BusinessObject1("First Value");
     // Subscribe
     bo.Publish += new BusinessObject.PublishObject(bo_Publish);
     bo.Update("Second Value");
     // UnSubscribe
     bo.Publish -= new BusinessObject.PublishObject(bo_Publish);
     bo.Update("Third Value");
     // Subscribe multiple
     bo.Publish += new BusinessObject.PublishObject(bo_Publish);
     bo.Publish += new BusinessObject.PublishObject(bo_Publish2);
     bo.Update("Fourth Value");
     // UnregisterAllMessages
     bo.UnsubcribeAll();
     bo.Update("Fifth Value");
  }

  static void bo_Publish(BusinessObject sender, EventArgs args)
  {
     if (sender is BusinessObject1)
     {
        BusinessObject1 bo1 = (BusinessObject1)sender;
        BusinessObject1.PublishBusinessObject1EventArgs args1 =
           (BusinessObject1.PublishBusinessObject1EventArgs)args;
        Console.WriteLine("Updated {0} to {1}", args1.oldValue, bo1.Value);
     }
  }

  static void bo_Publish2(BusinessObject sender, EventArgs args)
  {
     if (sender is BusinessObject1)
     {
        BusinessObject1 bo1 = (BusinessObject1)sender;
        BusinessObject1.PublishBusinessObject1EventArgs args1 =
           (BusinessObject1.PublishBusinessObject1EventArgs)args;
        Console.WriteLine("Second handler detected updated of {0} to {1}", args1.oldValue, bo1.Value);
     }
  }
}

abstract class BusinessObject
{
  public delegate void PublishObject(BusinessObject sender, EventArgs args);
  public event PublishObject Publish;
  // PublishEvent
  protected void Update(EventArgs args)
  {
     if (Publish != null)
        Publish(this, args);
  }
  public void UnsubcribeAll()
  {
     Publish = null;
  }
}

class BusinessObject1 : BusinessObject
{
  public class PublishBusinessObject1EventArgs : EventArgs
  {
     public string oldValue;
     public PublishBusinessObject1EventArgs(string oldValue)
     {
        this.oldValue = oldValue;
     }
  }

  public delegate void PublishBusinessObject1(BusinessObject1 sender, PublishBusinessObject1EventArgs args);

  public string Value {get; private set;}

  public BusinessObject1(string value)
  {
     this.Value = value;
  }

  public void Update(string newValue)
  {
     PublishBusinessObject1EventArgs args = new PublishBusinessObject1EventArgs(Value);
     Value = newValue;
     base.Update(args);
  }
}

Edit: If you don't want your business objects to have to inherit from a common base class (as you suggested in your comment) you can make a few modifications so that EventBus stands more independently, but you still don't need to re-implement all the event registration framework to do this:

class Program
{
  static void Main(string[] args)
  {
     BusinessObject1 bo = new BusinessObject1("First Value");
     // Subscribe
     EventBus.Publish += new EventBus.PublishObject(EventBus_Publish);
     bo.Update("Second Value");
     // UnSubscribe
     EventBus.Publish -= new EventBus.PublishObject(EventBus_Publish);
     bo.Update("Third Value");
     // Subscribe multiple
     EventBus.Publish += new EventBus.PublishObject(EventBus_Publish);
     EventBus.Publish += new EventBus.PublishObject(EventBus_Publish2);
     bo.Update("Fourth Value");
     // UnregisterAllMessages
     EventBus.UnsubcribeAll();
     bo.Update("Fifth Value");
  }

  static void EventBus_Publish(object sender, EventArgs args)
  {
     if (sender is BusinessObject1)
     {
        BusinessObject1 bo1 = (BusinessObject1)sender;
        BusinessObject1.PublishBusinessObject1EventArgs args1 =
           (BusinessObject1.PublishBusinessObject1EventArgs)args;
        Console.WriteLine("Updated {0} to {1}", args1.oldValue, bo1.Value);
     }
  }

  static void EventBus_Publish2(object sender, EventArgs args)
  {
     if (sender is BusinessObject1)
     {
        BusinessObject1 bo1 = (BusinessObject1)sender;
        BusinessObject1.PublishBusinessObject1EventArgs args1 =
           (BusinessObject1.PublishBusinessObject1EventArgs)args;
        Console.WriteLine("Second handler detected updated of {0} to {1}", args1.oldValue, bo1.Value);
     }
  }
}

static class EventBus
{
  public delegate void PublishObject(object sender, EventArgs args);
  public static event PublishObject Publish;
  // PublishEvent
  public static void Update(object sender, EventArgs args)
  {
     if (Publish != null)
        Publish(sender, args);
  }
  public static void UnsubcribeAll()
  {
     Publish = null;
  }
}

class BusinessObject1
{
  public class PublishBusinessObject1EventArgs : EventArgs
  {
     public string oldValue;
     public PublishBusinessObject1EventArgs(string oldValue)
     {
        this.oldValue = oldValue;
     }
  }

  public delegate void PublishBusinessObject1(BusinessObject1 sender, PublishBusinessObject1EventArgs args);

  public string Value { get; private set; }

  public BusinessObject1(string value)
  {
     this.Value = value;
  }

  public void Update(string newValue)
  {
     PublishBusinessObject1EventArgs args = new PublishBusinessObject1EventArgs(Value);
     Value = newValue;
     EventBus.Update(this, args);
  }
}

Edit 2: By the way, if you want more control over the subscription process, you can get more control there too by defining custom event accessors as described at http://msdn.microsoft.com/en-us/library/bb882534.aspx:

static class EventBus
{
  public delegate void PublishObject(object sender, EventArgs args);
  private static List<PublishObject> subscribers = new List<PublishObject>();
  public static event PublishObject Publish
  {
     add
     {
        subscribers.Add(value);
        Console.WriteLine("Added subscriber {0}.{1}", value.Method.DeclaringType.Name, value.Method.Name);
     }
     remove
     {
        bool result = subscribers.Remove(value);
        Console.WriteLine("Removed subscriber {0}.{1} ({2})", value.Method.DeclaringType.Name, value.Method.Name, result ? "success" : "failure");
     }
  }
  // PublishEvent
  public static void Update(object sender, EventArgs args)
  {
     foreach (PublishObject p in subscribers)
     {
        Console.WriteLine("Publishing to {0}.{1}", p.Method.DeclaringType.Name, p.Method.Name);
        p.Invoke(sender, args);
     }
  }
  public static void UnsubcribeAll()
  {
     subscribers.Clear();
  }
}
BlueMonkMN
  • 25,079
  • 9
  • 80
  • 146
  • Well, this way when object wants to publish event it need to have defined delegate and UnsubscribeAll() method. In my case object of any class can publish event just simply calling EventBus instance and than passing event like: eventBus.PublishEvent(this, stringEvent); In your case I'll need all my objects to inherit some base class with these defined methods. Correct me if I'm wrong. – Vajda Jul 09 '11 at 18:30
  • With a few changes (shown above) you can eliminate the inheritance, and keep pretty much the same general implementation. – BlueMonkMN Jul 10 '11 at 00:20
  • This is little better. But again, with my example I have more flexibility. Since I only need to implement IListener interface for every listener of events. And to initialize method which will be invoked when event is fired. In your way I need to know from which components my component will receive events, and that to register to every of them. In my case I just need to register for specific event type and will receive all events from any component firing that type of event. I figured out how to solve this. But thanks a lot, you helped me to better understand problem. :) – Vajda Jul 10 '11 at 14:06
1

Well, as a first suggestion, it looks to me as if you're trying to implement this as a singleton. Otherwise, what would the

private static EventBus instance;

be good for? But the private instance member is never assigned anywhere, that's one thing I'd suggest you should fix. For reference, here's a really good article on various implementations of singletons. If you've got access to .net4 I'd suggest you use the LazySingleton3 approach.

The only other thing that comes to mind is, this looks like it could be a use-case for Generics. Have a look at the EventHandler<TEventArgs> Delegate.

Apart from that, I can't recommend much more since I don't exactly understand what you're trying to do.

EDIT

Have a look at the accepted answer on this question. It contains a link to a blog post of someone who implemented this several years ago. Seems like you don't need to re-invent the wheel.

Community
  • 1
  • 1
takrl
  • 6,356
  • 3
  • 60
  • 69