Перехват исключений, сгенерированных в конструкторе целевого объекта блока Using - PullRequest
5 голосов
/ 29 июля 2010
using(SomeClass x = new SomeClass("c:/temp/test.txt"))
{
...
}

Внутри блока использования все в порядке с обработкой исключений как обычно. Но что, если конструктор SomeClass может выдать исключение?

Ответы [ 5 ]

5 голосов
/ 29 июля 2010

Поместите свое использование в try catch f.e.

try
{
   using(SomeClass x = new SomeClass("c:/temp/test.txt"))
   {
       ...
   }
}
catch(Exception ex)
{
   ...
}
4 голосов
/ 29 июля 2010

Да , это будет проблемой, когда конструктор выдает исключение. Все, что вы можете сделать, это обернуть блок using в блок try / catch. Вот почему вы должны сделать это таким образом.

использование блоков - это всего лишь синтаксический сахар, и каждый компилятор заменяет блок эквивалентным блоком try / finall. Единственная проблема заключается в том, что компилятор не заключает конструктор в блок try. Ваш код после компиляции будет иметь следующее преобразование в IL.

        //Declare object x of type SomeClass.
        SomeClass x;

        //Instantiate the object by calling the constructor.
        x = new SomeClass("c:/temp/test.txt");

        try
        {
            //Do some work on x.
        }
        finally
        {
            if(x != null)
                x.Dispose();
        }

Как видно из кода, объект x не будет создан в случае, если конструктор выдает исключение, и элемент управления не будет перемещаться дальше от точки возведения исключения, если не обработан.

Я только что опубликовал сообщение в своем блоге на эту тему вчера вечером.

Мне просто интересно, почему C # дизайнеры не заворачивали объект строительство в пределах блока try который по моему мнению должен был быть сделано.

EDIT

Я думаю, что нашел ответ, почему C # не переносит конструкцию объекта в блок try, сгенерированный вместо блока using.

Причина проста. Если вы поместите и объявление, и экземпляр в блок try, тогда the object would be out of scope for the proceeding finally block и код не будет компилироваться, потому что для блока finally объект вряд ли существует. Если вы только заключаете конструкцию в блок try и сохраняете объявление перед блоком try, даже в этом случае он не скомпилируется, поскольку находит you're trying to use an assigned variable.

1 голос
/ 29 июля 2010

Я собрал программу быстрого тестирования, чтобы проверить это, и кажется, что метод Dispose не вызывается, когда в конструктор выдается исключение;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            using (OtherClass inner = new OtherClass())
            {
                Console.WriteLine("Everything is fine");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
        Console.Read();
    }
}

class OtherClass : IDisposable
{
    public OtherClass()
    {
        throw new Exception("Some Error!");
    }

    void IDisposable.Dispose()
    {
        Console.WriteLine("I've disposed my resources");
    }
}

Вывод:

Некоторая ошибка!

Если вы не выбросите исключение ..

Вывод:

Все нормально

Я распорядился своими ресурсами

Предположительно, это потому, что объект никогда не создавался, поэтому нечего вызывать Dispose.

Я не уверен, что произойдет, если конструктор уже выделил некоторые ресурсы, которые обычно требуют надлежащей очистки с помощью Dispose, и после этого возникла исключительная ситуация.

0 голосов
/ 31 июля 2010

Это не должно быть проблемой с хорошо разработанным классом. Запомните общий вопрос:

public class HoldsResources : IDisposable
{
    public HoldsResources()
    {
        // Maybe grab some resources here
        throw new Exception("whoops");
    }
}

using (HoldsResources hr = new HoldsResources())
{
}

Итак, вопрос в том, что вы должны сделать с ресурсами, выделенными конструктором HoldsResources, прежде чем он вызовет исключение?

Ответ таков: вы не должны ничего делать с этими ресурсами. Это не твоя работа. Когда было решено, что HoldsResources будет иметь ресурсы, это привело к обязательству надлежащим образом распоряжаться ими. Это означает блок try / catch / finally в конструкторе, и это означает правильную реализацию IDisposable для удаления этих ресурсов в методе Dispose.

Ваша обязанность заключается в использовании блока using для вызова его метода Dispose, когда вы используете экземпляр. Ничего другого.

0 голосов
/ 29 июля 2010

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

В этом примере показан ctor, который предотвратитпри утечке, когда что-то идет не так, применяются те же правила, когда вы распределяете одноразовые материалы внутри фабричного метода.

class Sample
{
  IDisposable DisposableField;

  ...

  public Sample()
  {
    var disposable = new SomeDisposableClass();
    try
    {
       DoSomething(disposable);
       DisposableField = disposable;
    }
    catch
    {
       // you have to dispose of it yourself, because
       // the exception will prevent your method/ctor from returning to the caller.
       disposable.Dispose();
       throw;
    }
  }
}

Редактировать : мне пришлось сменить образец с фабрики на ктор, потому чтоочевидно, это было не так легко понять, как я надеялся.(Судя по комментариям.)

И, конечно, причина этого такова: когда вы звоните на фабрику или ctor, вы можете распоряжаться только его результатом.Когда вызов проходит, вы должны предположить, что все в порядке.

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

...