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


Использование(...) оператор является синтаксическим сахаром для 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, являются владельцами неуправляемых ресурсов? Дискуссия с моим другом показала, что ответ был отрицательным. (Я также прочитал некоторые темы в разделе using этого форума, некоторые очень хорошие знания там).

5 15

5 ответов:

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

Но если вы делаете это сами, убедитесь, что вы все делаете правильно. Блок 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. Поскольку они уже выполняют преобразование кода, не похоже, что это добавит так много дополнительного сложность.

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

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

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

Если вам нужно явно обрабатывать различные исключительные случаи, которые могут произойти во время выполнения инструкции, вы можете заменить using на try/catch/finally и вызвать Dispose() явно в finally. Или вы можете поместить try/catch вокруг блока 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();
    }
}

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

using(Timer timer = new Timer())
{
    //do stuff
}

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

FileStream fs = null;
try
{
    fs = File.Open(path);
    // ...
}
finally
{
    if (fs != null)
        fs.Dispose();
}
Таким образом, правильно использовать using, Если вам не нужен catch, но если вы это делаете, то вы должны просто использовать нормальный try/catch/finally.