Monday, 26 November 2012

IDisposable and object initialisers

Background...

The "using" statement is just syntax trick. The resulting compiled code from a "using" statement is actually a try-catch statement.

This...


    public class MyClass
    {
        public void MyMethod()
        {
            using (MyDisposableClass myDisposableClass = new MyDisposableClass())
            {
                myDisposableClass.DoSomething();
            }
        }
    }


...is converted by the compiler to this...


    public class MyClass
    {
        public void MyMethod2()
        {
            MyDisposableClass myDisposableClass = null;
            try
            {
                myDisposableClass = new MyDisposableClass();
            }
            catch
            {
                myDisposableClass.Dispose();
            }
        }
    }


Problem...

Consider the following code:

    public class MyClass
    {
        public void MyMethod()
        {
            using (MyDisposableClass myDisposableClass = new MyDisposableClass
                {
                    MyProperty = string.Empty
                })
            {
                myDisposableClass.DoSomething();
            }
        }
    }


This can cause a problem.

The problem is caused by the object initialiser. Remember, the object initialiser is just syntactic sugar. "Under the covers" (the actual compiled code), the object will be created as normal with its constructor and then the properties would be assigned afterwards.

So the use of the object initialiser means the object will be created outside the scope of the using statement. This in turn means the scope of the try-catch statement generated by the compiler would be incorrect. If the object is not created inside the scope of the using statement (and therefore the try-catch statement), should there be an error during the creation and initialisation, then "Dispose" will never be called. If Dispose is never called, the object will not be disposed in a timely manner. This means you may leak resources.

FxCop will advise you of the problem with an error like the following:

CA2000 : Microsoft.Reliability : In method 'MyClass.MyMethod()', object '<>g__initLocal0' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal0' before all references to it are out of scope.

The correct code is as follows:


    public class MyClass
    {
        public void MyMethod()
        {
            using (MyDisposableClass myDisposableClass = new MyDisposableClass())
            {
                myDisposableClass.MyProperty = string.Empty;
                myDisposableClass.DoSomething();
            }
        }
    }


The lesson is not to use object initialisers in combination with "using" statements.

About Me