Thursday, January 21, 2010

using using

In C++ we have what is called ‘deterministic finalization’ which means that the lifetime of an object is predictable. In .NET however we don’t; the garbage collector takes care of deleting objects and when this happens is not defined.

This makes all kinds of nice patterns we were able to use in C++ impossible. Fortunately, there is a solution in .NET by using the IDisposable interface. In .NET all classes that manage some expensive resource implement this interface and it is up to you, the developer, to make sure the Dispose() method is called appropriately. This is actually not so nice, for example think of exceptions that may occur before you’re able to call Dispose() ( and when you happen to be a SharePoint dev you must really ensure proper use of Dispose() or you may quickly exhaust your server)

Fortunately the C# compiler helps here by providing the using() statement that ensures a call to Dispose() on your object, no matter what.

Having said this I come to my point, which is that we can also use IDisposable nicely for other purposes then freeing up resources.

For example, in good old MFC we had a class called CWaitCursor which one could put at the entry of a lengthy UI method. When the CWaitCursor object was constructed the cursor would show a wait cursor, when the method finished, the object would go out of scope and the cursor would be reset, nice. In .NET we don’t have it, but we can use IDisposable and using() to achieve the same behavior.

So what we want is to write:

using (WaitCursor w = new WaitCursor())
{
    // lengthy operation:
    Thread.Sleep(2000);
}


In order to achieve this, we make a simple class as follows:



class WaitCursor : IDisposable
{
    private Cursor _prev = Cursors.Default;
    public WaitCursor()
    {
        _prev = Cursor.Current;
        Cursor.Current = Cursors.WaitCursor;
    }
    
    ~WaitCursor()
    {
        Cursor.Current = _prev;
    }
    #region IDisposable Members
    public void Dispose()
    {
        Cursor.Current = _prev;
        GC.SuppressFinalize(this);
    }
    #endregion
}


Of course this is a very simple example but it shows an alternate use of using() and IDisposable and can be applied to many more scenarios. It sure beats writing try{} catch{} finally{} to set/unset all sorts of state.

No comments:

Post a Comment