Как мне обработать все исключения в классе C #, где и ctor, и финализатор генерируют исключения? - PullRequest
2 голосов
/ 20 апреля 2010

Как я могу обработать все исключения для класса, подобного следующему при определенных обстоятельствах?

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

Я пробовал это, но это не работает:

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

  // t is still null
}

Я также пытался использовать «using», но он не обрабатывает исключение, выброшенное из ~ Test ();

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

Есть идеи, как мне обойтись?

Ответы [ 3 ]

5 голосов
/ 20 апреля 2010

Прежде всего, финализатор должен никогда генерировать исключение. Если это произойдет, что-то пошло катастрофически не так, и приложение должно упасть. Финализатор также никогда не должен вызывать Dispose () напрямую. Финализаторы предназначены только для освобождения неуправляемых ресурсов, поскольку управляемые ресурсы могут даже не находиться в допустимом состоянии после запуска Финализатора. Управляемые ссылки уже будут очищены сборщиком мусора, поэтому вам нужно только утилизировать их в Dispose, а не в Finalizer.

Тем не менее, исключение в утилизации должно быть перехвачено, если вы явно вызываете Dispose. У меня нет хорошего понимания того, как случай «использования» не выбрасывает исключение. Тем не менее, Dispose действительно не должен вызывать исключения, если вы можете избежать этого. В частности, Dispose, который выдает исключение после использования блока, будет «перезаписывать» любое исключение, которое может возникнуть внутри блока с исключением Dispose.


Некоторые дополнительные справочные материалы здесь

3 голосов
/ 20 апреля 2010

Я думаю, что часть ответа в том, что вы не должны обрабатывать исключения в этих случаях.

Вы должны перехватывать исключения только в том случае, если вы можете восстановить их или добавить дополнительную информацию в исключение и перебросить. Вы не должны ловить все исключения. Пусть код выше в стеке вызовов обрабатывает как можно больше исключений.

1 голос
/ 20 апреля 2010

У меня есть пара наблюдений.

Во-первых, не создавайте исключения из Dispose. На самом деле, я бы даже сказал, что никогда не скажу никогда. Разработчики .NET были уверены, что Dispose всегда будет успешным и по уважительной причине. Было бы неудобно помещать вызов в try-catch каждый раз, и это, безусловно, уменьшило бы читаемость.

Во-вторых, и это часто обсуждается, избегайте создания исключений из конструкторов. Исключения, связанные с проверкой состояния, такие как ArgumentException или IndexOutOfRangeException, допустимы, поскольку они обычно генерируются из-за нарушений предварительных условий и обычно являются результатом ошибки программирования. Но непредсказуемые исключения, такие как SqlException, в значительной степени вынудили бы вызывающую программу обернуть конструктор в блок try-catch. Опять же, это приводит к неудобным сценариям кодирования. Но, что более важно, это может привести к незначительным утечкам ресурсов в сценариях, когда конструктор выделяет неуправляемые ресурсы, а затем выдает исключение перед возвратом нового экземпляра вызывающей стороне. Без ссылки на экземпляр вызывающая сторона не может вызвать Dispose.

...