Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructor Exceptions in dispposable object

Tags:

c#

Consider folowing classes:

class C1 : IDisposable {...}

class C2 : IDisposable {...}

sealed class C3 : IDisposable
{
   public C3()
   {
      c1 = new C1();
      throw new Exception(); //oops!
   }
   ~C3()
   {
      //What can we do???
   }
   public void Dispose()
   {
      if ( c1 != null ) c1.Dispose();
      if ( c2 != null ) c2.Dispose();
   }

   private C1 c1;
   private C2 c2;
   //assume that this class does not contains native resources
}

Now, assume we correctly use disposable object:

using (var c3 = new C3())
{
}

What about this code snippet?

In this case we can't call Dispose method, because our object never exists.

We know, that in this case finalizer would be called, but there we can dispose only CriticalFinilizedObjects and we can't dispose C1 or C2 objects.

My solution is pretty simple:

sealed class C3 : IDisposable
{
   public C3()
   {
      try {
      c1 = new C1();
      throw new Exception(); //oops!
      c2 = new C2();
      }
      catch(Exception)
      {
        DisposeImpl();
        throw;
      }
   }
   ~C3()
   {
      //Not deterministically dispose detected!
      //write to log! 
      //Invalid class usage. Or not??
   }
   public void Dispose()
   {
      DisposeImpl();
   }
   private void DisposeImpl()
   {
     if ( c1 != null ) c1.Dispose();
     if ( c2 != null ) c2.Dispose();
     GC.SuppressFinalize(this); //all resources are released
   }

   private C1 c1;
   private C2 c2;
}

This solution may vary in some details but I think you can understand key principle: if constructor throws an exception we forcedly release acquired resources and suppress finalization.

Have any one other idias, suggestions or better solutions?

P.S. Herb Sutter in his blogpost (http://herbsutter.wordpress.com/2008/07/25/constructor-exceptions-in-c-c-and-java/) opened the question but he does not proposed a solution.

like image 457
Sergey Teplyakov Avatar asked Jan 19 '10 08:01

Sergey Teplyakov


1 Answers

What you're proposing is the conclusion I came to as well when thinking about this issue.

In summary, do as much work as needed to get the object in a usable state in the constructor, but clean up any expensive managed resources you allocate in an exception handler if you can't complete it successfully.

It is idiomatic in .NET to use the constructor to get the object in a state where it can be used. Some people will suggest the use of a simple constructor followed by an Initialize method where any 'real' work is done to get the object in the right state, but I cannot think of a single framework class that does this, so it's not an intuitive pattern for .NET developers to follow, and thus should not be done in .NET irrespective of whether it is a reasonable convention in other languages and platforms.

I think it's a fairly rare case anyway - typically a class that wraps a disposable resource will take it as a constructor parameter rather than creating it itself.

like image 145
Greg Beech Avatar answered Oct 03 '22 21:10

Greg Beech