I've an application where I'm not able to remove event handlers because I don't know when the last reference will be freed.
My application contains a PropertyChanged event source that is put into a container class that also implements INotifyPropertyChanged. This hierarchy contains more than 6 levels. Each instance of a level could be placed into multiple other instances. That's the reason why I couldn't determine when to free those instances.
The instances on the lowest level will live for the whole application runtime. This causes that all other instances will not be freed and I got a memory leak.
To avoid this event driven memory leak I tried to use WeakEventManager(TEventSource, TEventArgs). This class is only available in .Net 4.5 and because of compatibility to existing hardware I’ve to use .Net 4.0.
In .Net 4.0 is a PropertyChangedEventManager available that should do the same for INotifyPropertyChanged.
My classes are freed correctly.
But there is still a memory leak.
I simplified my application to the following code that produces a memory leak:
// This code will force the memory leak
while (true)
{
var eventSource = new StateChangedEventSource();
var eventReceiver = new StateChangedEventReceiver();
PropertyChangedEventManager.AddListener(eventSource, eventReceiver, string.Empty);
}
public class EventSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
public class EventReceiver : IWeakEventListener
{
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
return true;
}
}
Yes I know there is no RemoveListener call. I couldn’t determine when an instance is never used and could be freed. If I knew that I could use normal event registration and deregistration. In that case I don’t have to use the PropertyChangedEventManager.
What is the problem of my sample code? Why does it produce a memory leak?
Edit 2014/02/17:
I tried the WeakEventManager(TEventSource, TEventArgs) and .Net 4.5 and the problem still exists.
var eventSource = new EventSource();
var i = 0;
while (true)
{
var eventReceiver = new EventReceiver();
// --> Use only one of the following three lines. Each of them will produce a memory leak.
WeakEventManager<EventSource, PropertyChangedEventArgs>.AddHandler(eventSource, "PropertyChanged", eventReceiver.OnEvent);
PropertyChangedEventManager.AddListener(eventSource, eventReceiver, string.Empty);
WeakEventManager<EventSource, EventArgs>.AddHandler(eventSource, "SomeOtherEvent", eventReceiver.OnSomeOtherEvent);
// <--
++i;
if (i == 1 << 18)
{
Thread.Sleep(10);
GC.Collect(2);
Thread.Sleep(10);
i = 0;
}
}
public class EventSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler<EventArgs> SomeOtherEvent;
}
public class EventReceiver : IWeakEventListener
{
public void OnSomeOtherEvent(object sender, EventArgs args)
{
}
public void OnEvent(object sender, PropertyChangedEventArgs args)
{
}
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
return true;
}
}
This code compiled using .Net 4.5 do also run out of memory. I got the hint using the Thread.Sleep construct here.
I don't think the isue with WeakEventManager<,> is specific to non-WPF, as i can reproduce a memory leak in a WPF application as well.
The problem lies with the management of the event table. For each subscription, the WeakEventManager creates an entry in a table. This entry and table are (by necessity) strong-referenced.
The problem is, that by default, the WeakEventManager does not clean up the records. You have to call RemoveHandler. But beware. It's not thread safe. If you call it from another thread it may fail (doesn't throw an exception, you'll just experience that there's still a memory leak). When called from a finalizer, it doesn't work reliably either.
I also investigated into the source code and found that while it contains logic to do cleanup on AddHandler and when an event is received, it is disabled by default (see WeakEventManager.cs => WeakEventTable.CurrentWeakEventTable.IsCleanupEnabled). Also, you can't access the Cleanup method since the methods and properties necessary to do so are private or internal. So you can't even create subclass to access these methods / modify the behavior.
WeakEventManager<,> is broken
So basically (as far as i can understand) WeakEventManager<,> is broken by design (it keeps a StrongReference to the subscriber table-entry).
Instead of fixing a MemoryLeak it will only reduce the MemoryLeak (the event source and listener can be garbage collected, but the entry for the event subscription is not => new memory leak). Of course the memory leak introduced by WeakEventManager<,> is small.
Based on the information at msdn and codeproject I realized that the WeakEventManager(TEventSource, TEventArgs) class will only work in WPF applications. I'm using WinForms what's the reason why it seems not to work.
I decided to create my own WeakEventManager that works without using the build in WeakEventManager provided by the .Net framework.
The implementation of my WeakEventManager uses a background thread to clean up all instances. Maybe there's a better solution but this solution will work correctly in my application.
public static class ThreadedWeakEventManager
{
private static readonly TimeSpan CleanupInterval = TimeSpan.FromSeconds(1.0);
private static readonly List<IInternalWeakEventManager> EventManagers = new List<IInternalWeakEventManager>();
private static volatile bool _performCleanup = true;
static ThreadedWeakEventManager()
{
new Thread(Cleanup) { IsBackground = true, Priority = ThreadPriority.Lowest }.Start();
}
public static void AddHandler<TEventArgs>(object eventSource, string eventName, EventHandler<TEventArgs> eventHandler)
where TEventArgs : EventArgs
{
var weakEventManager = new InternalWeakEventManager<TEventArgs>(eventSource, eventName, eventHandler);
lock (EventManagers)
{
EventManagers.Add(weakEventManager);
}
}
public static void AddPropertyChangedHandler(INotifyPropertyChanged eventSource, EventHandler<PropertyChangedEventArgs> eventHandler)
{
AddHandler(eventSource, "PropertyChanged", eventHandler);
}
public static void AddCollectionChangedEventHandler(INotifyCollectionChanged eventSource, EventHandler<NotifyCollectionChangedEventArgs> eventHandler)
{
AddHandler(eventSource, "CollectionChanged", eventHandler);
}
public static void RemoveHandler<TEventArgs>(object eventSource, string eventName, EventHandler<TEventArgs> eventHandler)
where TEventArgs : EventArgs
{
if (eventSource == null || string.IsNullOrWhiteSpace(eventName) || eventHandler == null)
{
return;
}
lock (EventManagers)
{
EventManagers.RemoveAll(item => object.ReferenceEquals(item.EventData.EventSource, eventSource) && item.EventName.Equals(eventName) && eventHandler.Method.Equals(item.EventData.EventHandlerMethodInfo));
}
}
public static void RemovePropertyChangedHandler(INotifyPropertyChanged eventSource, EventHandler<PropertyChangedEventArgs> eventHandler)
{
RemoveHandler(eventSource, "PropertyChanged", eventHandler);
}
public static void RemoveCollectionChangedEventHandler(INotifyCollectionChanged eventSource, EventHandler<NotifyCollectionChangedEventArgs> eventHandler)
{
RemoveHandler(eventSource, "CollectionChanged", eventHandler);
}
public static void CancelCleanup()
{
_performCleanup = false;
}
private static void Cleanup()
{
while (_performCleanup)
{
Thread.Sleep(CleanupInterval);
lock (EventManagers)
{
for (var i = EventManagers.Count - 1; i >= 0; --i)
{
var item = EventManagers[i];
if (item.EventData.IsGarbageCollected)
{
item.UnwireEvent();
EventManagers.RemoveAt(i);
}
}
}
}
}
private interface IInternalWeakEventManager
{
string EventName { get; }
IWeakEventData EventData { get; }>
void UnwireEvent();
void OnEvent(object sender, EventArgs args);
}
private class InternalWeakEventManager<TEventArgs> : IInternalWeakEventManager
where TEventArgs : EventArgs
{
private static readonly MethodInfo OnEventMethodInfo = typeof(InternalWeakEventManager<TEventArgs>).GetMethod("OnEvent");
private EventInfo _eventInfo;
private Delegate _onEvent;
public InternalWeakEventManager(object eventSource, string eventName, EventHandler<TEventArgs> eventHandler)
{
this.EventData = new WeakEventData<TEventArgs>(eventSource, eventHandler);
this.WireEvent(eventSource, eventName);
}
public string EventName
{
get { return this._eventInfo.Name; }
}
public IWeakEventData EventData { get; private set; }
public void UnwireEvent()
{
var eventSource = this.EventData.EventSource;
if (eventSource == null)
{
return;
}
this._eventInfo.RemoveEventHandler(eventSource, this._onEvent);
}
public void OnEvent(object sender, EventArgs args)
{
this.EventData.ForwardEvent(sender, args);
}
private void WireEvent(object eventSource, string eventName)
{
this._eventInfo = eventSource.GetType().GetEvents().FirstOrDefault(item => item.Name == eventName);
if (this._eventInfo == null)
{
throw new InvalidOperationException(string.Format("The event source type {0} doesn't contain an event named {1}.", eventSource.GetType().FullName, eventName));
}
this._onEvent = Delegate.CreateDelegate(this._eventInfo.EventHandlerType, this, OnEventMethodInfo);
this._eventInfo.AddEventHandler(eventSource, this._onEvent);
}
}
private interface IWeakEventData
{
bool IsGarbageCollected { get; }
object EventSource { get; }>
MethodInfo EventHandlerMethodInfo { get; }
void ForwardEvent(object sender, EventArgs args);
}
private class WeakEventData<TEventArgs> : IWeakEventData
where TEventArgs : EventArgs
{
private readonly WeakReference _eventSource;
private readonly WeakReference _eventTargetInstance;
public WeakEventData(object eventSource, EventHandler<TEventArgs> eventHandler)
{
this._eventSource = new WeakReference(eventSource);
this._eventTargetInstance = new WeakReference(eventHandler.Target);
this.EventHandlerMethodInfo = eventHandler.Method;
}
public object EventSource
{
get { return this._eventSource.Target; }
}
public MethodInfo EventHandlerMethodInfo { get; private set; }
public bool IsGarbageCollected
{
get
{
return !this._eventSource.IsAlive || !this._eventTargetInstance.IsAlive;
}
}
public void ForwardEvent(object sender, EventArgs args)
{
var target = this._eventTargetInstance.Target;
if (target != null)
{
this.EventHandlerMethodInfo.Invoke(target, new[] { sender, args });
}
}
}
}
Memory leak in WeakEventManager can occur if AddListener is called from different threads.
Just call AddListener from the main UI thread and inner "CleanUp" will work fine.
Related
I am attaching to a 3rd party, long living Deleted event publisher, which ends up keeping my possibly short lived objects alive due to the event handler. The Deleted event is most likely never triggered, I just have to handle things if it is. It's not obvious where to un-subscribe from the Deleted event, thus I would like a weak reference to it so my objects can be GC'd.
I have seen a lot of very elaborate ways to create weak event handlers, but the following snippet seems to do the trick, at least in the provided test snippet. Is this just insane or can it work?
(http://diditwith.net/CommentView,guid,aacdb8ae-7baa-4423-a953-c18c1c7940ab.aspx says under "A First Stab" that a similar snippet "(...) isn't robust enough to be used with an event (...)", why not?)
public static class WeakEvent
{
private class WeakEventHolder<TArgs> where TArgs : EventArgs
{
private readonly WeakReference _handler;
public WeakEventHolder(Action<object, TArgs> handler)
{
_handler = new WeakReference(handler);
}
public void Handle(object sender, TArgs args)
{
Action<object, TArgs> handler = (Action<object, TArgs>)_handler.Target;
if (handler != null)
handler(sender, args);
}
}
public static EventHandler MakeHandler(Action<object, EventArgs> handler)
{
return new WeakEventHolder<EventArgs>(handler).Handle;
}
}
Test class
[TestFixture]
public class Tests
{
public class Publisher
{
public EventHandler Event;
public void Raise()
{
if (Event != null)
Event(this, EventArgs.Empty);
}
}
public class Target
{
public Target(Publisher publisher)
{
publisher.Event += WeakEvent.MakeHandler(HandleEvent);
}
public void HandleEvent(object sender, EventArgs args)
{
System.Diagnostics.Trace.WriteLine("HandleEvent");
}
}
[Test]
public void Test()
{
Publisher publisher = new Publisher();
WeakReference wref = new WeakReference(new Target(publisher));
GC.Collect();
publisher.Raise();
Assert.False(wref.IsAlive);
}
}
Because Action<object, TArgs> handler maybe garbage collected before it's target is. Here's a unit test that exposes the problem:
public class Bar
{
public void Foo(object sender, EventArgs args)
{
}
}
[Test]
public void ActionIsNotGCedBeforeTarget()
{
Bar bar = new Bar();
Action<object, EventArgs> action = bar.Foo;
WeakReference weakRef = new WeakReference(action);
action = null;
GC.Collect();
Assert.IsTrue(weakRef.IsAlive); // Will be false
}
I declare a subscription to event in:
public class MainClass
{
public void btndel_bar_Click(object sender, RoutedEventArgs e)
{
SomeClass sc = new SomeClass();
sc.FieldUpdate += new SomeClass.FieldUpdateHandler(sc_FieldUpdate);
}
void sc_FieldUpdate(object sender, ValueEventArgs e)
{
MessageBox.Show(e.Smth_property);
}
}
And here is I want to listen event:
public class Someclass
{
public delegate void FieldUpdateHandler(object sender, ValueEventArgs e);
public event FieldUpdateHandler FieldUpdate;
void Somemethod()
{
string str = "Steel";
ValueEventArgs args = new ValueEventArgs(str);
FieldUpdate(this, args);
}
}
A class which carries data:
public class ValueEventArgs : EventArgs
{
private string smth;
public ValueEventArgs(string smth)
{
this.smth = smth;
}
public string Smth_property
{
get { return smth; }
}
}
I always have FieldUpdate=null. How to solve it?
You're calling Somemethod() in the constructor, before the calling code gets a chance to add the event handler.
Therefore, the event is still null.
The moment you create the object of SomeClass your event would get reinitialized.
Make your event a static so that multiple objects of SomeClass would share it
public static event FieldUpdateHandler FieldUpdate;
I've read articles about delegates and events and after reading I always I thought to make all operations again. I did all over again and it works! Consequently I done something wrong when I did at the beginning of.
All I am trying to do is implementing the observer pattern.
So, I came up with this solution:
We have a PoliceHeadQuarters whose primary job is to send notifications to all those who are subscribed to it. Consider that the DSP, Inspector and SubInspector classes are subscribed to PoliceHeadQuarters.
Using Events and Delegates I wrote
public class HeadQuarters
{
public delegate void NewDelegate(object sender, EventArgs e);
public event EventHandler NewEvent;
public void RaiseANotification()
{
var handler = this.NewEvent;
if (handler != null)
{
handler(this, new EventArgs());
}
}
}
public class SubInspector
{
public void Listen(object sender, EventArgs e)
{
MessageBox.Show(string.Format("Event Notification received by sender = {0} with eventArguments = {1}", sender, e.ToString()));
}
}
public class Inspector
{
public void Listen(object sender, EventArgs e)
{
MessageBox.Show(string.Format("Event Notification received by sender = {0} with eventArguments = {1}", sender, e.ToString()));
}
}
and this is how I invoked it
var headQuarters = new HeadQuarters();
var SubInspector = new SubInspector();
var Inspector = new Inspector();
headQuarters.NewEvent += Inspector.Listen;
headQuarters.NewEvent += SubInspector.Listen;
headQuarters.RaiseANotification();
so, both Inspector and SubInspector classes get notification whenever there the function RaiseANotification() is invoked.
It seems that the DotNet Framework 4, 4.5 supports a new way called IObserver and IObservable.
Can anyone give me a super simple example using IObservable and IObserver pattern for the above scenario? I googled only to find the available examples in the internet too bloated and difficult to understand.
My hinch: (probably i think it's wrong)
class DSP : IObserver //since it observes the headquarters ?
class PoliceHeadQuarters: IObservable // since here's where we send the notifications ?
Thanks in advance.
EDIT: Somebody also said that the MSDN documentation is also incorrect for IObservable #
IObservable vs Plain Events or Why Should I use IObservable?.
Here's a modification of MSDN example to fit your framework:
public struct Message
{
string text;
public Message(string newText)
{
this.text = newText;
}
public string Text
{
get
{
return this.text;
}
}
}
public class Headquarters : IObservable<Message>
{
public Headquarters()
{
observers = new List<IObserver<Message>>();
}
private List<IObserver<Message>> observers;
public IDisposable Subscribe(IObserver<Message> observer)
{
if (!observers.Contains(observer))
observers.Add(observer);
return new Unsubscriber(observers, observer);
}
private class Unsubscriber : IDisposable
{
private List<IObserver<Message>> _observers;
private IObserver<Message> _observer;
public Unsubscriber(List<IObserver<Message>> observers, IObserver<Message> observer)
{
this._observers = observers;
this._observer = observer;
}
public void Dispose()
{
if (_observer != null && _observers.Contains(_observer))
_observers.Remove(_observer);
}
}
public void SendMessage(Nullable<Message> loc)
{
foreach (var observer in observers)
{
if (!loc.HasValue)
observer.OnError(new MessageUnknownException());
else
observer.OnNext(loc.Value);
}
}
public void EndTransmission()
{
foreach (var observer in observers.ToArray())
if (observers.Contains(observer))
observer.OnCompleted();
observers.Clear();
}
}
public class MessageUnknownException : Exception
{
internal MessageUnknownException()
{
}
}
public class Inspector : IObserver<Message>
{
private IDisposable unsubscriber;
private string instName;
public Inspector(string name)
{
this.instName = name;
}
public string Name
{
get
{
return this.instName;
}
}
public virtual void Subscribe(IObservable<Message> provider)
{
if (provider != null)
unsubscriber = provider.Subscribe(this);
}
public virtual void OnCompleted()
{
Console.WriteLine("The headquarters has completed transmitting data to {0}.", this.Name);
this.Unsubscribe();
}
public virtual void OnError(Exception e)
{
Console.WriteLine("{0}: Cannot get message from headquarters.", this.Name);
}
public virtual void OnNext(Message value)
{
Console.WriteLine("{1}: Message I got from headquarters: {0}", value.Text, this.Name);
}
public virtual void Unsubscribe()
{
unsubscriber.Dispose();
}
}
public class Program
{
public static void Main(string[] args)
{
Inspector inspector1 = new Inspector("Greg Lestrade");
Inspector inspector2 = new Inspector("Sherlock Holmes");
Headquarters headquarters = new Headquarters();
inspector1.Subscribe(headquarters);
inspector2.Subscribe(headquarters);
headquarters.SendMessage(new Message("Catch Moriarty!"));
headquarters.EndTransmission();
Console.ReadKey();
}
}
Another suggestion - you probably want to consider leveraging the reactive extensions library for any code using IObservable. The nuget package is Rx-Main and the homepage for it is here: http://msdn.microsoft.com/en-us/data/gg577609.aspx
Update: ReactiveX has been translated to many platforms and languages and is now managed as an open source project. Here is the landing page.
This will save you a lot of boilerplate code. Here's a super simple example:
var hq = new Subject<string>();
var inspectorSubscription = hq.Subscribe(
m => Console.WriteLine("Inspector received: " + m));
var subInspectorSubscription = hq.Subscribe(
m => Console.WriteLine("Sub Inspector received: " + m));
hq.OnNext("Catch Moriarty!");
It will output:
Inspector received: Catch Moriarty!
Sub Inspector received: Catch Moriarty!
Reactive Extensions is a big subject, and a very powerful library - worth investigating. I recommend the hands-on lab from the link above.
You would probably want to embed those subscriptions within your Inspector, SubInspector immplementatinos to more closely reflect your code. But hopefully this gives you an insight into what you can do with Rx.
This has turned out to be quite a lengthy question, so thank you in advance to all those who give up their time to read it and comment/answer :)
Edits
This Question has been majorly simplified.
Example code is now a complete, simple program
I am using an observer pattern implemented through interfaces:
public interface IObserver<in T>where T:EventArgs
{
void Update(object sender, T e);
}
public interface ISubject<in T, TU>where TU:EventArgs
{
event EventHandler<TU> Notify;
T State { set; }
void Attach(Action<object,TU> callback);
void Detach(Action<object, TU> callback);
}
I have created two simple classes which implement these interfaces
The MyObserver object will simply output a string to the console window when a Notify event is raised in the MySubject object.
public class MyObserver:IObserver<TestEventArgs>
{
private ISubject<bool, TestEventArgs> _subject;
public MyObserver(ISubject<bool, TestEventArgs> subject)
{
_subject = subject;
}
public void Subscribe()
{
_subject.Attach(Update);
}
public void Unsubscribe()
{
_subject.Detach(Update);
}
public void Update(object sender, TestEventArgs e)
{
Console.WriteLine(e.TestMessage);
}
}
public class MySubject:ISubject<bool, TestEventArgs>
{
public void ObservableEvent(string message)
{
InvokeNotify(message);
}
private void InvokeNotify(string message)
{
EventHandler<TestEventArgs> handler = Notify;
if(handler != null)
{
handler(this, new TestEventArgs(message));
}
}
public event EventHandler<TestEventArgs> Notify;
public bool State
{
set { throw new NotImplementedException(); }
}
public void Attach(Action<object, TestEventArgs> callback)
{
Notify += new EventHandler<TestEventArgs>(callback);
}
public void Detach(Action<object, TestEventArgs> callback)
{
Notify -= new EventHandler<TestEventArgs>(callback);
}
}
public class TestEventArgs:EventArgs
{
public TestEventArgs(string message)
{
TestMessage = message;
}
public string TestMessage { get; private set; }
}
This test program shows that:
before myObserver has subscribed to the event no message is output to the Console window.
after myObserver has subscribed to the Notify event the message is output to the Console window.
after myObserver has UNsubscribed from the Notify event the message is still output to the Console window
static void Main(string[] args)
{
MySubject mySubject = new MySubject();
MyObserver myObserver = new MyObserver(mySubject);
//we have not subscribed to the event so this should not be output to the console
mySubject.ObservableEvent("First Test");
myObserver.Subscribe();
//we are now subscribing to the event. This should be displayed on the console window
mySubject.ObservableEvent("Second Test");
myObserver.Unsubscribe();
//We have unsubscribed from the event. I would not expect this to be displayed
//...but it is!
mySubject.ObservableEvent("Third Test");
Console.ReadLine();
}
The issue I'm having is that the unsubscribe process is not working.
I really don't understand why.
Questions
Why is the unsubscribe process not working?
What happens when comparing 2 event handlers? How are they defined as equal or not? This may lead to an answer to why the invocation list Contains method always returns false.
I suspect your problem is that this code:
public void Attach(Action<object, TestEventArgs> callback)
{
Notify += new EventHandler<TestEventArgs>(callback);
}
Actually allocates a new object, as does the corresponding Detach code. So what's being detached isn't the same thing as what's being attached.
I'm not sure, but you might be able to fix it by changing your Attach and Detach so that they're:
void Attach(EventHandler<TU> callback);
void Detach(EventHandler<TU> callback);
And in the client code:
public void Attach(EventHandler<TestEventArgs> callback)
{
Notify += callback;
}
public void Detach(EventHandler<TestEventArgs> callback)
{
Notify -= callback;
}
I haven't actually tried to compile this, but it looks like it should work.
Or, if the compiler can do the type conversion:
public void Attach(Action<object, TestEventArgs> callback)
{
Notify += callback;
}
Might be worth a shot.
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.
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.