开发者

How do I handle all the exceptions in a C# class where both ctor and finalizer throw exceptions?

开发者 https://www.devze.com 2022-12-27 23:44 出处:网络
How can I handle all exceptions for a class similar to the following under certain circumstances? class Test : IDisposable {

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?


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


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消