Here are a few notes about best practices related to:
- IDisposable and an object’s Dispose() method
- The using statement
- Disposal of BackgroundWorker objects
(NOTE: BackgroundWorker object is no longer the preferred mechanism for doing work on a background thread in C#, given that the language supports task-basked asynchrony with the async/await constructs. However, many legacy applications still make use of the BackgroundWorker class).
Q: What’s the goal of the using statement and IDisposable (Dispose) interface?
A: (short) To tell an object when it can clean up unmanaged resources that it might be hanging onto
- .NET code can make use of managed resources (e.g. instantiate another .NET object) or unmanaged resources (e.g. open a file to read from it)
- Managed resources are released by the garbage collector (GC) automatically
- Note that this is non-deterministic, i.e. you can’t predict when an object will be GC’d
- To release an unmanaged resource, code typically follows this pattern:
- Release resource in finalizer (~ syntax). Finalizer called during GC, so unmanaged resource is then released when object is being GC’d
- Optionally, can support IDisposable (Dispose method)
- Client calls Dispose before object is GC’d to released unmanaged resource earlier than normal GC
- Allows for deterministic destruction
- using statement automates calling of Dispose on an object
- Classes implementing Dispose will still get GC’d normally at a later time
- If Dispose was called first, code typically tells GC not to call its finalizer, since it’s already done stuff done by the finalizer (GC.SuppressFinalization)
- If client failed to call Dispose, finalizer runs normally, so unmanaged resources then get cleaned up before GC
- Objects with finalizers take a little bit longer to be GC’d
- If an object has a finalizer, the GC process takes just a little bit longer, since the GC needs to first finalize the object before releasing its memory
- This is why it’s a good idea for Dispose logic to call GC.SuppressFinalization. (See http://csharp.2000things.com/2013/09/16/931-objects-with-finalizers-take-longer-to-garbage-collect/ )
- Here’s how IDispose is typically implemented – http://csharp.2000things.com/2011/10/11/430-a-dispose-pattern-example/
Q: When should I use the using statement?
A: Typically, you should use the using statement to invoke Dispose on any object that implements IDisposable
Q: What happens if I don’t call Dispose or use the using statement?
A: (short) Unmanaged resources are (typically) released a bit later than they otherwise would be
- If you don’t call Dispose on an object that implements IDisposable, it typically hangs onto unmanaged resources until it is GC’d and then releases them
- Depending on the type of resource, the first object may block access to the resource until it’s released
- Failing to use using (or call Dispose) typically doesn’t lead to a memory leak. Rather, it just means that resources are released a bit later
Q: Should I use a using statement for a BackgroundWorker object?
A: (short) Yes, since BackgroundWorker has Dispose method (although calling Dispose doesn’t actually do anything)
- It’s okay to use using on BackgroundWorker, since it does implement IDisposable
- BackgroundWorker, however, doesn’t actually do anything when Dispose is called. Its parent class, Component, detaches from its ISite container, but this is only relevant in Windows Forms.
- Calling Dispose does suppress finalization, which means that the BackgroundWorker will be GC’d a little bit sooner. This is reason enough to use using on the BackgroundWorker.
- The using statement for a BackgroundWorker does nothing with the BackgroundWorker’s event handlers (i.e. it doesn’t detach any event handlers)
Q: Should I detach event handlers in the handler for RunWorkerCompleted?
A: (short) No, you (typically) don’t need to explicitly detach event handlers for a BackgroundWorker
- In .NET, if two objects reference each other, but no other “root” object references either of them, they do both get garbage collected
- If we have a WPF form that has a class-level reference to a BackgroundWorker
- Assume that we instantiate the BackgroundWorker when user does something on the form and attach handlers (methods in form) to that instance
- Form now has ref to BackgroundWorker (class-level ref) and BW has ref to form (via the handlers)
- When form closes, if main application no longer has a reference to the form, both the form and the BackgroundWorker will be properly garbage collected even though they reference each other
- You do need to detach handlers if you have a BackgroundWorker that is meant to live longer than the object that owns the handlers
- g. If we had an application-level BackgroundWorker and forms that attached handlers to its DoWork or RunWorkerCompleted events. If the BW was meant to live after the form closes, you’d want to have the form detach its handlers when it closed.