The using Statement and Members

Posted in software by Christopher R. Wirz on Sat Feb 18 2012



The primary use of the IDisposable interface is to release resources - such as unmanaged objects (such as database connections or native library objects). Normally, the garbage collector will release the memory allocated to a managed object when that object is no longer used, but this is not predictable or guaranteed. Also, the garbage collector has no knowledge of unmanaged resources. Good thing we have IDisposable.

When the life of an IDisposable object is scoped, it is best practice to use the using statement. The using statement calls the Dispose method on the object.

Suppose we start with the following code (comments omitted for brevity):


using System;
namespace Practice
{
	class DisposeCaller : IDisposable
	{
	    private bool disposed = false;
		public void Dispose() // Implementation of IDisposable
		{
		    if (disposed)
		    {
				Console.WriteLine("DisposeCaller.Dispose() called AGAIN");
				return;
		    }
	        disposed = true;
			Console.WriteLine("DisposeCaller.Dispose() called");
		}
	}

	class DisposeWrapper
	{
		private DisposeCaller disposeCaller = new DisposeCaller();

		public void Method()
		{
			Console.WriteLine("DisposeWrapper.Method() called");
			using (disposeCaller);
		}
		~DisposeWrapper()
		{
			Console.WriteLine("~DisposeWrapper() Called");
			disposeCaller?.Dispose();
		}
	}

	class Program
	{
		static void Main(string[] args)
		{
			var dw = new DisposeWrapper();
			dw.Method();
			dw.Method();
		}
	}
}

What will happen to the DisposeCaller member when Method() is called? disposeCaller gets disposed - meaning there is no point to call Method() again. Let's run the code and see...

Practice.exe

DisposeWrapper.Method() called
DisposeCaller.Dispose() called
DisposeWrapper.Method() called
DisposeCaller.Dispose() called AGAIN

What I find particularly interesting is that the finalizer (~DisposeWrapper()) is never hit. I guess the best practice is to implement IDisposable and appropriately constrain the lifecycle of objects using scope. Maybe your luck will be different if you try it yourself.