Because your application can crash otherwise, that’s why.
While it’s always a good idea to explicitly dispose everything that is disposable, we can usually get away without disposing UI controls because
a. Complex controls aren’t created often – a single instance is often reused.
b. The finalizer kicks in and saves the day.
If you’re creating multiple instances of a System.Windows.Forms.DataGridView, however, watch out, because (b) doesn’t happen at all if you don’t call Dispose or otherwise cause the control to be destroyed.
When debugging a software crash dump recently, I found that the crash occurred because there was an exception when attempting to show the exception handler dialog, and that was because the application had run out of window handles. Being a managed application, that meant that the application was holding to way more UI control instances than is normal.
A quick look at the objects in the heap showed tons of instances of a certain type of control. Looking at the code, it was clear that while the control is created often, there is no code that holds on to the instances indefinitely – each new instance clears all references to the old instance. Sure, the previous instance was not being disposed, but I skipped over that, assuming that the problem must be someone holding on to the instances, or otherwise the finalizer would have cleared things up.
Dumping the gcroots for a random sample of those objects showed a common chain.
1: DOMAIN(002869F0):HANDLE(Pinned):2013e8:Root: 02ed5250(System.Object)->
2: 01efce64(System.Collections.Generic.Dictionary`2[[System.Object, mscorlib],[System.Collections.Generic.List`1[[Microsoft.Win32.SystemEvents+SystemEventInvokeInfo, System]], mscorlib]])->
3: 01efd3d8(System.Collections.Generic.Dictionary`2+Entry[[System.Object, mscorlib],[System.Collections.Generic.List`1[[Microsoft.Win32.SystemEvents+SystemEventInvokeInfo, System]], mscorlib]])->
4: 01efe464(System.Collections.Generic.List`1[[Microsoft.Win32.SystemEvents+SystemEventInvokeInfo, System]])->
The delegate type in line 7 was a dead giveaway; looking for references to that type using Reflector showed the following code inside DataGridView’s OnHandleCreated method.
1: protected override void OnHandleCreated(EventArgs e)
3: // A bunch of other code
4: SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(this.OnUserPreferenceChanged);
That line right there is why the crash happened.
When the DataGridView control’s window handle gets created, it subscribes to an event from a static class (SystemEvents.UserPreferenceChanged). It does unsubscribe from it in the OnHandleDestroyed method, but that gets called only if the control is properly disposed.
Now if you don’t call Dispose, the control remains subscribed to the event; that counts as a strong reference to the control, and it therefore cannot be garbage collected and finalized. Which means that the control and all its associated resources are not going to be released until the application shuts down (or the AppDomain unloads).
In the crashing application, there were other controls subscribed to events from the DataGridView, so it in turn prevented garbage collection and finalization of those controls, and eventually, they used up a really large number of window handles, causing the application to crash when it tried to create a new control.
It’s rather strange if you think about it; the finalizer is supposed to be mechanism to cleanup if Dispose is not called on an object, but for a DataGridView, the finalizer won’t run unless you call Dispose, as otherwise a strong reference to the object will exist. It won’t run after you call Dispose either – the Dispose implementation calls SuppressFinalize(this).