Event Bus implementation question
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开发者_C百科 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.
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
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();
}
}
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.
精彩评论