Подтвердить что ты не робот

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

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

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

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


}

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

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

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

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

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