Search code examples
c#using-statementobject-initializers

Can I safely use object initializers inside using statements?


I'm wondering if the use of object initializers inside using statements somehow prevents the correct disposal of the resource declared inside them, e.g.

using (Disposable resource = new Disposable() { Property = property })
{
   // ...
}

I've read that object initializers are nothing but synctatic sugar, which the compiler translates to something similar to the following code:

MyClass tmp = new MyClass(); 
tmp.Property1 = 1;
tmp.Property2 = 2;
actualObjectYouWantToInitialize = tmp;

Even if I may appear as a confused ignorant, I'd like to ask for clarifications. Does the fact that the initialized object is (as far as I understand) a pointer to another object (which is also a pointer, as far as I know), interfere with the disposal of the resource done by a using statement?


Solution

  • The main (only?) danger is that if setting Property fails (i.e. throws an exception), then resource won't be Disposed.

    using (Disposable resource = new Disposable() { Property = property })
    {
       // ...
    }
    

    Normally exceptions within a using block are fine - since the using is syntactic sugar for a try .. finally.

    The issue here is that when Property = property is executing you effectively aren't yet 'inside' the using block. This is essentially no different to what happens if the constructor threw an exception.

    The thing that the finally block will be trying to Dispose is resource - but resource was never set - resource (as shown in your actualObjectYouWantToInitialize example) is set after the properties have all been set .

    https://dotnetfiddle.net/vHeu2F shows how this might occur in practice. Notice that Dispose was logged once, even though there are two using blocks.

    using System;
    
    namespace Bob
    {
        public class Disposable : IDisposable
        {
            private int _property;
    
            public int Property { get => _property; set => throw new Exception(); }
    
            public void Dispose()
            {
                Console.WriteLine("Disposed");
            }
        }
    
        public class Program
        {
            public static void Main()
            {
                Console.WriteLine("1");
                using (var resource = new Disposable())
                {
                    Console.WriteLine("2");
                }
                Console.WriteLine("3");
    
    
                using (var resource = new Disposable() { Property = 1 })
                {
                    Console.WriteLine("4");
    
                }
                Console.WriteLine("5");
            }
        }
    }
    

    CA2000 may be helpful in detecting this issue.