Reputation: 20016
Other questions and answers advocate using the IDisposable
interface to unsubscribe from events that an object subscribes to in its constructor. What I'm failing to understand is why the Dispose()
method gets called in the first place.
Consider the code (verbatim) from this example:
public class DetailViewModel : IDisposable
{
public DetailViewModel(MyDetailModel detailModel)
{
// Retain the Detail Model
this.model = detailModel;
// Handle changes to the Model not coming from this ViewModel
this.model.PropertyChanged += model_PropertyChanged; // Potential leak?
}
public void Dispose()
{
this.model.PropertyChanged -= model_PropertyChanged;
}
}
So supposing that this model is referenced by the DataContext
of some window, then it should have no references when that window is closed and garbage collected. But the event detailModel.PropertyChanged
will still hold a reference to the delegate (which is why we're unsubscribing in the first place). And each delegate is implemented as a compiler-generated class which will hold a reference to the instance (the DetailViewModel
) in its auto-generated _target
field.
So does _target
not count as a reference to the DetailViewModel
?
I see two cases.
DetailViewModel
lives as long as the event it's subscribing to does, and Dispose
won't benefit anything since it won't be called until after the subscribed object gets garbage collected.DetailViewModel
gets disposed of which would call the event removal code. Ok, no more space leak. But if you don't implement IDisposable
, and the event gets fired and tries to call your delegate, which now points to a deleted object, why doesn't the program crash? Does the CLR just swallow the exception?I've only been coding C# for about a month, so if I'm really fundamentally misunderstanding something, please let me know.
Upvotes: 1
Views: 260
Reputation: 6864
Firstly an object can't point to a deleted object. Objects are only garbage collected when no other objects (considered live) are referenced by the object.
As per the MS docs...
https://msdn.microsoft.com/en-us/library/system.idisposable.dispose.aspx?f=255&MSPPError=-2147217396
... Dispose should be safe to call at any time! Regardless of when or how many times it has been previously called.
Generally Dispose() is usually called when YOU call it. If you know the object is not going to be used again and you haven't used a Using statement and the object implements Dispose(), you should call dispose.
You don't usually implement Dispose unless your object has un-managed resources or has members that implement Dispose(). In practice I have found, if you want your code to behave itself across platforms (not just desktop, mono, Windows Phone, Windows CE etc), it's a good idea to decouple objects (i.e. set stuff to null) in the Dispose, to try and give the Garbage Collector as much help as possible to collect your objects.
Unsubscribing from events can be important because you don't want the object processing events from objects which remain live. Quite often as in your case, both observer (the event consumer) and the subject/observeee (the object exposing the event) have equal life times and go out of scope at the same time.
Usually the GC can recognise graphs of disassociated objects and collect them as a group.
I'd also recommend reading...
https://msdn.microsoft.com/en-us/library/fs2xkftw.aspx
...which gives further guidence on implementing dispose.
Upvotes: 1