views:

138

answers:

3

How can I handle all exceptions for a class similar to the following under certain circumstances?

class Test : IDisposable {
  public Test() {
    throw new Exception("Exception in ctor");  
  }
  public void Dispose() {
    throw new Exception("Exception in Dispose()");
  }
  ~Test() {
    this.Dispose();
  }
}

I tried this but it doesn't work:

static void Main() {
  Test t = null;
  try {
    t = new Test();
  }
  catch (Exception ex) {
    Console.Error.WriteLine(ex.Message);
  }

  // t is still null
}

I have also tried to use "using" but it does not handle the exception thrown from ~Test();

static void Main() {
  try {
    using (Test t = new Test()) { }
  }
  catch (Exception ex) {
    Console.Error.WriteLine(ex.Message);
  }
}

Any ideas how can I work around?

+5  A: 

First off, a Finalizer should never throw an exception. If it does, something has gone catastrophically wrong and the app should crash hard. A Finalizer should also never call Dispose() directly. Finalizers are only for releasing unmanaged resources, as managed resources may not even be in a valid state once the Finalizer runs. Managed references will already be cleaned up by the garbage collector, so you only need to Dispose them in your Dispose, not in your Finalizer.

That said, an Exception in Dispose should be caught if you call Dispose explicitly. I don't have a good understanding of how the 'using' case did not throw the exception. That said, Dispose really shouldn't be throwing exceptions either, if you can avoid it. In particular, a Dispose that throws an exception after a using block will 'overwrite' any exception that could occur inside the using block with the Dispose exception.


Some additional reference material here

Dan Bryant
The problem is that if the constructor throws an exception, I have no pointer to the "partial" object thus I cannot call the Dispose() explicitly. In addition, with "using" statement the exception from the constructor is caught - but when GC calls the finalizer there's another exception that cannot be handled.
Frank
If an object throws an exception in its constructor after having partially allocated unmanaged resources, then it's a design flaw in the object. Unfortunately there's nothing you can do about that. If an object absolutely had to throw an exception during construction, a better design would be to 'roll back' the unmanaged allocations, if possible. Better yet, move the logic that could fail from the constructor to some other Init method.
Dan Bryant
+2  A: 

I think part of the answer is you shouldn't handle exceptions in these cases.

You should only catch exceptions if you can recover from them, or if you can add additional information to the exception and rethrow. You should not catch every exception. Let the code higher up in the call stack handle as many exceptions as possible.

John Saunders
+1  A: 

I have a couple of observations.

First, avoid throwing exceptions from Dispose. In fact, I would almost go as far as saying never. .NET developers have been conditioned to expect that Dispose will always succeed and for good reason. It would be awkward wrapping the call in try-catch everytime and it certainly would reduce readability.

Second, and this a matter that is debated frequently, avoid throwing exceptions from constructors. Exceptions related to the validation of state such ArgumentException or IndexOutOfRangeException are okay because they are typically generated because of pre-condition violations and are usually a result of a programming error. But, unpredictable exceptions such as SqlException would pretty much force a caller to wrap the constructor in a try-catch block. Again, this leads to awkward coding scenarios. But, more importantly this can lead to subtle resource leaks in scenarios where the constructor allocates unmanaged resources then throws an exception before returning the new instance to the caller. Without the instance reference the caller cannot call Dispose.

Brian Gideon