Let's say that A links B. Also, say you think you are done with B and expect this to be garbage collection.
Now, if A is reachable [1], B will not be garbage collected, despite the fact that "you did with it." This is essentially a memory leak [2]
If B subscribes to an event in A , then we have the same situation: A has a link to B through the delegate of the event handler.
So when is the problem? Only when the link object is available, as described above. In this case, there may be a leak when the Foo instance is no longer used:
class Foo { Bar _bar; public Foo(Bar bar) { _bar = bar; _bar.Changed += BarChanged; } void BarChanged(object sender, EventArgs e) { } }
The reason there might be a leak is because the Bar instance passed in the constructor may have a longer life than Foo using it. A signed event handler can then save Foo .
In this case, you need to provide a way to unsubscribe from the event so as not to get a memory leak. One way to do this is to let Foo implement IDisposable . The surface of this is that it clearly signals the class consumer that it needs to call Dispose () when it's done. Another way is to have separate Subscribe () and Unsubscribe () methods, but this does not convey type expectations - they are too optional to call and introduce a temporary connection.
My recommendation:
class sealed Foo : IDisposable { readonly Bar _bar; bool _disposed; ... public void Dispose() { if (!_disposed) { _disposed = true; _bar.Changed -= BarChanged; } } ... }
Or alternatively:
class sealed Foo : IDisposable { Bar _bar; ... public void Dispose() { if (_bar != null) { _bar.Changed -= BarChanged; _bar = null; } } ... }
On the other hand, if the referenced object is not available , then the leak cannot :
class sealed Foo { Bar _bar; public Foo() { _bar = new Bar(); _bar.Changed += BarChanged; } void BarChanged(object sender, EventArgs e) { } }
In this case, any instance of Foo will always outlive its configured instance of Bar . If Foo is not available, then there will be a Bar . A signed event handler cannot contain Foo here. The disadvantage of this is that if Bar is a dependency that needs to be mocked by unit test scripts, it cannot (in any pure way) be explicitly installed by the consumer, but must be introduced.
[1] http://msdn.microsoft.com/en-us/magazine/bb985010.aspx
[2] http://en.wikipedia.org/wiki/Memory_leak