Использование оператора и try-catch () - наконец повторение?

Оператор using (...) является синтаксическим сахаром для try {} finally {}.

Но если у меня будет оператор using, как показано ниже:

using (FileStream fs = File.Open(path))
{


}

Теперь я хочу поймать исключения, которые может вызвать открытие этого файла (и это код с довольно высоким риском, поскольку он может выйти из строя из-за окружающей среды), но если я напишу внутри try-catch, не будет ли это повторением? Когда код компилируется в IL, я предполагаю, что повтор будет удален, когда код будет JITted?

Однако я бы хотел поймать исключения, которые может вызвать открытие файла (поэтому я должен обернуть try-catch за пределы области действия оператора using), а также исключения для всего, что я делаю внутри блока using, поэтому я должен добавить try-catch внутри блока.

Похоже, я добавляю много повторений к тому, что, вероятно, делает внутри CLR. Добавляет ли CLR предложения catch?

Мой коллега утверждал, что оператор using беспорядочный (но это произошло из-за того, что одна строка была немного длинной из-за того, что я жестко их кодировал, так как мне нужно было изменить их очень быстро и у меня не было доступа к другим частям базы кода). Указанный коллега не использует оператор using, но есть ли какая-либо функциональная разница между оператором using и try-finally / try-catch-finally? Я видел один такой случай, когда у служб WCF есть малоизвестный угловой случай использования finally и возврата значений (что-то вроде finally). Решением было использование контрольного блока. Есть ли что-нибудь подобное в C #?

С другой стороны, все ли типы, реализующие IDisposale, являются владельцами неуправляемых ресурсов? Обсуждение с моим другом показало, что ответ отрицательный. (Я также прочитал несколько тем в разделе использования этого форума, там есть очень хорошие знания).


person GurdeepS    schedule 02.09.2009    source источник


Ответы (5)


Вы можете реализовать шаблон самостоятельно, если вам действительно нужно обработать некоторые исключения. Лично я все еще считаю, что проще (и, что более важно, яснее) просто обернуть using в блоке try / catch.

Но если вы делаете это сами, убедитесь, что у вас все получилось правильно. Блок Using также создает блок анонимной области видимости, чтобы ваша переменная могла быть собрана раньше. Метод .Dispose(), который вызывается в конце блока Using, очищает только неуправляемые ресурсы, поэтому любая память, содержащаяся в вашем объекте, может оставаться немного дольше. Вряд ли это серьезная проблема, но на всякий случай стоит вспомнить.

Итак, чтобы выполнить прямую адаптацию паттерна, ваш код должен выглядеть примерно так:

{
    FileStream fs;
    try
    {
        fs = File.Open(path);

    }
    catch (FileNotFoundException e) { /* ... */ }
    catch (IOException e) { /* ... */ }
    catch (Exception e) {/* ... */}
    finally
    {
        if (fs != null) fs.Dispose();
    }
}

Лично я бы хотел, чтобы Using был расширен для поддержки блоков Catch и Finally. Поскольку они уже выполняют преобразование кода, не похоже, что это добавит дополнительных сложностей.

person Joel Coehoorn    schedule 02.09.2009
comment
Хотя, наконец, используете ли поддержку? Откуда вы узнали об использовании анонимного блока области видимости? Хотелось бы узнать об этом побольше. Поэтому, когда я открываю файл в блоке using (например, FileSream.Open ()), это исключение всплывает. Если оператор using реализует try / finally, мне нужно обернуть это в try / catch, просто чтобы уловить. - person GurdeepS; 09.10.2009

Я бы предпочел сохранить оператор using и заключить его в try / catch. Внешний метод try / catch перехватит любые исключения, за которыми вам нужно следить, игнорируя Dispose (). Это дает дополнительное преимущество, так как защищает вас от самого себя, если вы позже проведете рефакторинг, чтобы переместить try / catch в другое место (например, в вызывающую функцию).

Что касается вашего вопроса об IDisposable: любой может реализовать это по любой причине, которая ему нравится. Нет никаких технических причин для его ограничения неуправляемыми ресурсами. (Следует ли ограничиваться неуправляемыми ресурсами в вашем коде - это другой вопрос).

person Jon B    schedule 02.09.2009

Самая большая разница между using и try-finally заключается в том, что using вызывает только метод Dispose(). Если вы реализуете свой собственный блок finally, он может выполнять другую логику, например, регистрацию, которая не будет включена в использование.

person David Basarab    schedule 02.09.2009

Если вам нужно явно обработать различные исключительные случаи, которые могут произойти во время выполнения оператора, вы можете заменить using на try/catch/finally и явно вызвать Dispose() в файле finally. Или вы можете поместить try/catch вокруг блока using, чтобы отделить исключительные обстоятельства от обеспечения утилизации.

Единственное, что делает using - это гарантирует, что Dispose () вызывается, даже если внутри блока возникло исключение. Это очень ограниченная, очень специфическая реализация общей структуры try/[catch]/finally.

Важно то, что ни один из этих вариантов не имеет реального влияния - пока он делает то, что вам нужно, читается и понятно, кого это волнует? Не похоже, что дополнительная попытка станет узким местом или чем-то еще!

Чтобы ответить на ваш последний вопрос - нет, IDisposable определенно не означает, что разработчик имеет дескрипторы для неуправляемых ресурсов. Это гораздо более простой и общий шаблон, чем этот. Вот один полезный пример:

public class Timer : IDisposable
{
    internal Stopwatch _stopwatch;
    public Timer()
    {
        this._stopwatch = new Stopwatch();
        this._stopwatch.Start();
    }

    public void Dispose()
    {
        this._stopwatch.Stop();
    }
}

Мы можем использовать это для измерения времени без необходимости явно полагаться на запуск и остановку вызова, используя:

using(Timer timer = new Timer())
{
    //do stuff
}
person Rex M    schedule 02.09.2009

Конструкция using является сокращением для блока try/finally. То есть компилятор генерирует:

FileStream fs = null;
try
{
    fs = File.Open(path);
    // ...
}
finally
{
    if (fs != null)
        fs.Dispose();
}

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

person AndyM    schedule 02.09.2009
comment
Нет ничего плохого в том, чтобы обернуть сам using в _2 _ / _ 3_. Это яснее, чем finally, потому что сразу видно, какие ресурсы освобождаются после выхода из блока, без необходимости смотреть на finally. - person Pavel Minaev; 03.09.2009