Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Occasions when the using statement and IDisposable should never be used

I was reading about this scenario where making use of the C# using statement can cause problems. Exceptions thrown within the scope of the using block can be lost if the Dispose function called at the end of the using statement was to throw an exception also. This highlights that care should be taken in certain cases when deciding whether to add the using statement.

I only tend to make use of using statements when using streams and the classes derived from DbConnection. If I need to clean up unmanaged resources, I would normally prefer to use a finally block.

This is another use of the IDisposable interface to create a performance timer that will stop the timer and log the time to the registry within the Dispose function. http://thebuildingcoder.typepad.com/blog/2010/03/performance-profiling.html

Is this good use of the IDisposable interface? It is not cleaning up resources or disposing of any further objects. However, I can see how it could clean up the calling code by wrapping the code it is profiling neatly in a using statement.

Are there times when the using statement and the IDisposable interface should never be used? Has implementing IDisposable or wrapping code in a using statement caused problems for you before?

Thanks

like image 557
fletcher Avatar asked Jul 20 '10 17:07

fletcher


2 Answers

I would say, always use using unless the documentation tells you not to (as in your example).

Having a Dispose method throw exceptions rather defeats the point of using it (pun intended). Whenever I implement it, I always try to ensure that no exceptions will be thrown out of it regardless of what state the object is in.

PS: Here's a simple utility method to compensate for WCF's behaviour. This ensures that Abort is called in every execution path other than when Close is called, and that errors are propogated up to the caller.

public static void CallSafely<T>(ChannelFactory<T> factory, Action<T> action) where T : class {
    var client = (IClientChannel) factory.CreateChannel();
    bool success = false;
    try {
        action((T) client);
        client.Close();
        success = true;
    } finally {
        if(!success) {
            client.Abort();
        }
    }
}

If you find any other funny behaviour cases elsewhere in the framework, you can come up with a similar strategy for handling them.

like image 91
Christian Hayter Avatar answered Oct 12 '22 09:10

Christian Hayter


The general rule of thumb is simple: when a class implements IDisposable, use using. When you need to catch errors, use try/catch/finally, to be able to catch the errors.

A few observations, however.

  1. You ask whether situations exist where IDisposable should not be used. Well: in most situations you shouldn't need to implement it. Use it when you want to free up resources timely, as opposed to waiting until the finalizer kicks in.

  2. When IDisposable is implemented, it should mean that the corresponding Dispose method clears its own resources and loops through any referenced or owned objects and calls Dispose on them. It should also flag whether Dispose is called already, to prevent multiple cleanups or referenced objects to do the same, resulting in an endless loop. However, all this is no guarantee that all references to the current object are gone, which means it will remain in memory until all references are gone and the finalizer kicks in.

  3. Throwing exceptions in Dispose is frowned upon and when it happens, state is possibly not guaranteed anymore. A nasty situation to be in. You can fix it by using try/catch/finally and in the finally block, add another try/catch. But like I said: this gets ugly pretty quickly.

  4. Using using is one thing, but don't confuse it with using try/finally. Both are equal, but the using-statement makes life easier by adding scoping and null-checks, which is a pain to do by hand each time. The using-statement translates to this (from C# standard):

    {
        SomeType withDispose = new SomeType();
        try
        {
             // use withDispose
        }            
        finally 
        {
            if (withDispose != null)
            {
                 ((IDisposable)withDispose).Dispose();
            }
        }
    }
    
  5. There are occasions where wrapping an object into a using-block is not necessary. These occasions are rare. They happen when you find yourself inheriting from an interface that inherits from IDisposable just in case a child would require disposing. An often-used example is IComponent, which is used with every Control (Form, EditBox, UserControl, you name it). And I rarely see people wrapping all these controls in using-statements. Another famous example is IEnumerator<T>. When using its descendants, one rarely sees using-blocks either.

Conclusion

Use the using-statement ubiquitously, and be judicious about alternatives or leaving it out. Make sure you know the implications of (not) using it, and be aware of the equality of using and try/finally. Need to catch anything? Use try/catch/finally.

like image 39
Abel Avatar answered Oct 12 '22 11:10

Abel