EDIT: After Joel Coehoorns excellent answer, I understand that I need to be more specific, so I modified my code to be closer to thing I\'m trying to unders
MSDN Reference
"To prevent your event handler from being invoked when the event is raised, unsubscribe from the event. In order to prevent resource leaks, you should unsubscribe from events before you dispose of a subscriber object. Until you unsubscribe from an event, the multicast delegate that underlies the event in the publishing object has a reference to the delegate that encapsulates the subscriber's event handler. As long as the publishing object holds that reference, garbage collection will not delete your subscriber object."
"When all subscribers have unsubscribed from an event, the event instance in the publisher class is set to null."
Contrary to what some other answers claim, events whose publisher's GC lifetime may exceed a subscriber's useful lifetime should be regarded as unmanaged resources. The term "unmanaged" in the phrase "unmanaged resource" doesn't mean "completely outside the world of managed code", but rather relates to whether objects require cleanup beyond that provided by the managed garbage collector.
For example, a collection may expose a CollectionChanged
event. If objects of some other type which subscribes to such an event are repeatedly created and abandoned, the collection may end up holding a delegate reference to each and every such object. If such creation and abandonment happens e.g. once per second (as might happen if the object in question were created in a routine that updates a UI window) the number of such references could grow by more than 86,000 for each day that the program was running. Not a big problem for a program which is never run for more than a few minutes, but an absolute killer for a program which could be run for weeks at a time.
It's really unfortunate that Microsoft didn't come up with a better event-cleanup pattern in vb.net or C#. There's seldom any reason why a class instance which subscribes to events shouldn't clean them up before it's abandoned, but Microsoft did nothing to facilitate such cleanup. In practice, one can get away with abandoning objects that are subscribed to events sufficiently often (because the event publisher will go out of scope around the same time as the subscriber) that the annoying level of effort necessary to ensure events get properly cleaned up doesn't seem worthwhile. Unfortunately, it's not always easy to predict all the cases where an event publisher might live longer than expected; if many classes leave events dangling, it's possible for huge amounts of memory to be uncollectable because one of the event subscriptions happens to belong to a long-lived object.
Addendum in response to edit
If X
were to subscribe to an event from Y
and then abandon all references to Y
, and if Y
became eligible for collection, X
would not prevent Y
from being collected. That would be a good thing. If X
were to keep a strong reference to Y
for the purpose of being able to dispose of it, such reference would prevent Y
from being collected. That might arguably not be such a good thing. In some situations it would be better for X
to keep a long WeakReference
(one constructed with the second parameter set to true
) to Y
rather than a direct reference; if the target of the WeakReference
is non-null when X
is Dispose
d, it will have to unsubscribe from Y
's event. If the target is null, it can't unsubscribe but it won't matter, because by then Y
(and its reference to X
) will have completely ceased to exist. Note that in the unlikely event that Y
dies and is resurrected, X
will still want to unsubscribe its event; using a long WeakReference
will ensure that can still happen.
While some would argue that X
shouldn't bother keeping a reference to Y
, and Y
should simply be written to use some sort of weak event dispatching, such behavior is not correct in the general case because there's no way for Y
to tell whether X
would do anything that other code might care about even if Y
holds the only reference to X
. It is entirely possible that X
might hold a reference to some strongly-rooted object, and might do something to that other object within its event handler. The fact that Y
holds the only reference to X
should not imply that no other objects are "interested" in X
. The only generally-correct solution is to have objects which are no longer interested in other objects' events notify the latter objects of that fact.
You don't need to unhook event handlers when disposing of an object, although you may want to. By that I mean that the GC will clean up event handlers just fine without any intervention on your part, however depending on the scenario you may want to remove those event handlers before the GC does in order to prevent the handler being called when you weren't expecting it.
In your example I think you have your roles reversed - class A
shouldn't really be unsubscribing event handlers added by others and has no real need to remove event handlers eiether, as it can instead just stop raising those events!
Suppose however that the situation is reversed
class A
{
public EventHandler EventHappened;
}
class B : IDisposable
{
A _a;
private bool disposed;
public B(A a)
{
_a = a;
a.EventHappened += this.HandleEvent;
}
public void Dispose(bool disposing)
{
// As an aside - if disposing is false then we are being called during
// finalization and so cannot safely reference _a as it may have already
// been GCd
// In this situation we dont to remove the handler anyway as its about
// to be cleaned up by the GC anyway
if (disposing)
{
// You may wish to unsubscribe from events here
_a.EventHappened -= this.HandleEvent;
disposed = true;
}
}
public void HandleEvent(object sender, EventArgs args)
{
if (disposed)
{
throw new ObjectDisposedException();
}
}
}
If its possible for A
to continue raising events even after B
has been disposed, and the event handler for B
could do something that may cause either an exception or some other unexpected behaviour if B
is disposed then its probably a good idea to unsubscribe from this event first.
The object A references B through EventHandler delegate(A has an instance of EventHandler wich references B). B don't have any reference to A. When A is set to null it will be collected and the memory will be freed. So you don't need to clear anything in this case.
I have added My comments in your sample code.
class A : IDisposable
{
public event EventHandler EventHappened
{
add
{
eventTable["EventHappened"] = (EventHandler)eventTable["EventHappened"] + value;
}
remove
{
eventTable["EventHappened"] = (EventHandler)eventTable["EventHappened"] - value;
}
}
public void Dispose()
{
//Amit: If you have only one event 'EventHappened',
//you can clear up the subscribers as follows
eventTable["EventHappened"] = null;
//Amit: EventHappened = null will not work here as it is
//just a syntactical sugar to clear the compiler generated backing delegate.
//Since you have added 'add' and 'remove' there is no compiler generated
//delegate to clear
//
//Above was just to explain the concept.
//If eventTable is a dictionary of EventHandlers
//You can simply call 'clear' on it.
//This will work even if there are more events like EventHappened
}
}
class B
{
public B(A a)
{
a.EventHappened += new EventHandler(this.HandleEventB);
//You are absolutely right here.
//class B does not store any reference to A
//Subscribing an event does not add any reference to publisher
//Here all you are doing is calling 'Add' method of 'EventHappened'
//passing it a delegate which holds a reference to B.
//Hence there is a path from A to B but not reverse.
}
public void HandleEventB(object sender, EventArgs args)
{
}
}
class C
{
public C(A a)
{
a.EventHappened += new EventHandler(this.HandleEventC);
}
public void HandleEventC(object sender, EventArgs args)
{
}
}
class Service
{
A _a;
void Start()
{
CustomNotificationSystem.OnEventRaised += new EventHandler(CustomNotificationSystemHandler)
_a = new A();
//Amit:You are right all these do not store any reference to _a
B b1 = new B(_a);
B b2 = new B(_a);
C c1 = new C(_a);
C c2 = new C(_a);
}
void CustomNotificationSystemHandler(args)
{
//Amit: You decide that _a has lived its life and must be disposed.
//Here I assume you want to dispose so that it stops firing its events
//More on this later
_a.Dispose();
//Amit: Now _a points to a brand new A and hence previous instance
//is eligible for collection since there are no active references to
//previous _a now
_a = new A();
}
}
b1,b2,c1,c2 will continue to exists as is, and I know they will now subscribed to previous instance of _a, and it's OK by me, BUT in that example, now, nobody references the previous instance of _a (b not holds reference to _a) and by my theory, previous instance of _a, now may be collected...or I'm missing something???
As explained through my comments in the above code, you are not missing anything here :)
BUT...as I know, it's not guaranteed when GC will collect the publisher so in theory, even if subscribers lifetime is greater then publishers, it can happen that subscriber is legal for collection, but publisher is still not collected (I don't know if within closest GC cycle, GC will be smart enough to collect publisher first and then subscriber.
Since publisher references subscriber, it can never happen that the subscriber becomes eligible for collection before the publisher but reverse can be true. If publisher gets collected before subscriber then, as you said, there is no problem. If the subscriber belongs to a lower GC generation than publisher then since publisher holds a reference to subscriber, GC will treat the subscriber as reachable and will not collect it. If both belong to same generation, they will be collected together.
since my subscriber do not have direct reference to publisher and can't unsubscribe the event, I would like to make publisher to implement IDisposable
Contrary to what some have suggested, I would recommend implementing dispose if at any point you are deterministically sure that the object is no longer required. Simply updating an object reference may not always lead to an object stop publishing events.
Consider the following code:
class MainClass
{
public static Publisher Publisher;
static void Main()
{
Publisher = new Publisher();
Thread eventThread = new Thread(DoWork);
eventThread.Start();
Publisher.StartPublishing(); //Keep on firing events
}
static void DoWork()
{
var subscriber = new Subscriber();
subscriber = null;
//Subscriber is referenced by publisher's SomeEvent only
Thread.Sleep(200);
//We have waited enough, we don't require the Publisher now
Publisher = null;
GC.Collect();
//Even after GC.Collect, publisher is not collected even when we have set Publisher to null
//This is because 'StartPublishing' method is under execution at this point of time
//which means it is implicitly reachable from Main Thread's stack (through 'this' pointer)
//This also means that subscriber remain alive
//Even when we intended the Publisher to stop publishing, it will keep firing events due to somewhat 'hidden' reference to it from Main Thread!!!!
}
}
internal class Publisher
{
public void StartPublishing()
{
Thread.Sleep(100);
InvokeSomeEvent(null);
Thread.Sleep(100);
InvokeSomeEvent(null);
Thread.Sleep(100);
InvokeSomeEvent(null);
Thread.Sleep(100);
InvokeSomeEvent(null);
}
public event EventHandler SomeEvent;
public void InvokeSomeEvent(object e)
{
EventHandler handler = SomeEvent;
if (handler != null)
{
handler(this, null);
}
}
~Publisher()
{
Console.WriteLine("I am never Printed");
}
}
internal class Subscriber
{
public Subscriber()
{
if(MainClass.Publisher != null)
{
MainClass.Publisher.SomeEvent += PublisherSomeEvent;
}
}
void PublisherSomeEvent(object sender, EventArgs e)
{
if (MainClass.Publisher == null)
{
//How can null fire an event!!! Raise Exception
throw new Exception("Booooooooommmm");
//But notice 'sender' is not null
}
}
}
If you run the above code, more often than not you will receive the 'Booooooooommmm'. Hence idea is that event publisher must stop firing events when we are sure that its life is up.
This can be done through Dispose method.
There are two ways to achieve this:
Benefit of 2 is that you release any reference to the subscribers, thereby enabling there collection (as I explained earlier even if the publisher is garbage but belongs to higher generation then it may still prolong collection of lower generation subscribers).
Though, admittedly, it will be quite rare that you experience the demonstrated behavior due to 'hidden' reachability of the publisher but as you can see benefits of 2 are clear and are valid for all event publishers especially long living ones (Singletons anybody!!!). This itself makes it worth to implement Dispose and go with 2.
I would have my class B implements IDisposable as well and in it's dispose routine, I would first check whether A is not null and then dispose A. By using this approach you have to just make sure to dispose the last of your class and the internals will handle all other dispose.