Является ли «использование» уместным в контексте, где нечего распоряжаться?

9

В C # usingоператор используется для детерминированного размещения ресурсов без ожидания сборщика мусора. Например, он может быть использован для:

  • Утилизируйте команды или соединения SQL,

  • Закрывайте потоки, освобождая основной источник как файл,

  • Бесплатные элементы GDI +,

  • и т.п.

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

Примеры:

  • MiniProfiler , написанный командой Stack Overflow, использует usingдля обозначения блоков в профиле:

    using (profiler.Step("Name goes here"))
    {
        this.DoSomethingUseful(i - 1);
    }
    

    Одним из альтернативных подходов было бы иметь два блока:

    var p = profiler.Start("Name goes here");
    this.DoSomethingUseful(i - 1);
    profiler.Stop(p);
    

    Другой подход заключается в использовании действий:

    profiler.Step("Name goes here", () => this.DoSomethingUseful(i - 1));
  • ASP.NET MVC также выбран usingдля форм:

    <% using (Html.BeginForm())
       { %>
           <label for="firstName">Name:</label>
           <%= Html.TextBox("name")%>
           <input type="submit" value="Save" />    
    <% } %>
    

Такое использование уместно? Как это оправдать, учитывая, что есть несколько недостатков:

  • Начинающие будут потеряны, так как такое использование не соответствует тому, которое объясняется в книгах и спецификации языка,

  • Код должен быть выразительным. Здесь страдает выразительность, поскольку надлежащее использование usingсостоит в том, чтобы показать, что позади есть ресурс, такой как поток, сетевое соединение или база данных, который должен быть освобожден без ожидания сборщика мусора.

Арсений Мурзенко
источник
2
Подход с наличием двух отдельных операторов страдает той же проблемой, что и наивное управление ресурсами без using: последнее (например profiler.Stop(p)) утверждение не гарантируется для выполнения в условиях исключений и потока управления.
1
@delnan: это объясняет, почему MiniProfiler избегал второго подхода. Но второго следует избегать во всех случаях, так как его трудно писать и поддерживать.
Арсений Мурзенко
1
Связанный: stackoverflow.com/questions/9472304
Роберт Харви
1
Связанные: stackoverflow.com/questions/2101524 и stackoverflow.com/questions/1095438
Роберт Харви
1
Связано: grabbagoft.blogspot.com/2007/06/…
Роберт Харви

Ответы:

7

Ваше последнее утверждение - «правильное использование использования - показать, что позади есть ресурс, такой как поток, сетевое соединение или база данных, которая должна быть освобождена без ожидания сборщика мусора», и причина, почему приведены в документации по интерфейсу IDisposable: http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

Основное использование этого интерфейса - освобождение неуправляемых ресурсов.

Итак, если ваш класс использует какие-либо неуправляемые ресурсы, не имеет значения, когда вы можете или не хотите, чтобы GC происходил - он не имеет ничего общего с GC, поскольку неуправляемые ресурсы не GC'ed ( https: // stackoverflow. ru / questions / 3607213 / что означает «управляемый против неуправляемых ресурсов в сети» .

Таким образом , цель « используя» не избежать ожиданий на GC, это заставить выпуск этих неуправляемых ресурсов в настоящее время , до того , как экземпляр класса выходит из области видимости , и это Доработка называется. Это важно по причинам, которые должны быть очевидны: неуправляемый ресурс может зависеть от других неуправляемых ресурсов, и если они утилизируются (или завершаются) в неправильном порядке, могут возникнуть плохие вещи.

Таким образом, если класс, который создается в блоке using, использует какие-либо неуправляемые ресурсы, тогда ответ - да - это уместно.

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

В любом случае, использование «using» подразумевает, что класс реализует IDisposable, поэтому он не нарушает выразительность кода; это действительно дает понять, что происходит на самом деле.

Максимус Минимус
источник
«Основное использование этого интерфейса - высвобождение неуправляемых ресурсов». Я говорю, что документация Microsoft отстой по этому вопросу. Возможно, именно так они и используют его в FCL, но разработчики приложений используют его для различных целей вот уже почти десять лет - некоторые по веским причинам, некоторые по не очень хорошим причинам. Но цитирование этого куска их документа вовсе не отвечает на вопрос ОП.
Джесси С. Слайсер
1
Обратите внимание на мой второй последний пункт - основная цель не должна быть единственной целью. IDisposable может быть реализован по любой причине, и он предоставляет известный интерфейс с ожидаемым поведением и стилем использования, поэтому, когда вы видите это, вы знаете, что есть что-то еще, что должно произойти, прежде чем экземпляр можно будет просто выпустить из области видимости. Дело в том, что: если он реализует IDisposable, тогда использование целесообразно; все остальное просто преамбула.
Максимус Минимус
1
Вы в основном говорите, что цель состоит не в том, чтобы избежать ожидания GC, а в том, чтобы избежать ожидания финализатора. Но я не вижу разницы: финализатор вызывается GC.
svick
GC работает недетерминированным образом - вы не знаете, когда он будет вызван. GC также просто вызывает финализатор, но он не выполняет никакой утилизации - сам фактический ресурс находится вне контроля GC. При использовании (1) объект правильно определен, и (2) известно, что удаление происходит в известный момент времени - когда он выходит из области использования блока, неуправляемый ресурс исчезает (или - в худшем случае - обрабатывается) к чему-то еще, что разрушит это). На самом деле, это просто придирки; прочитайте документацию, это все, что не нужно повторять.
Максимус Минимус
8

Использование usingподразумевает наличие Dispose()метода. Другие программисты будут предполагать, что такой метод существует на объекте. Следовательно, если предмет не является одноразовым, вы не должны использовать usingего.

Четкость кода - это король. Либо опустите using, либо внедрите IDisposableв объект.

Похоже, что MiniProfiler использует usingв качестве механизма «забор» в профилируемом коде. В этом есть некоторая заслуга; предположительно, MiniProfiler либо вызывает Dispose()останов таймера, либо таймер останавливается, когда объект MiniProfiler выходит из области видимости.

В более общем случае вы вызываете, usingкогда какая-то финализация должна выполняться автоматически. В документации html.BeginFormговорится, что когда метод используется в usingоператоре, он отображает закрывающий </form>тег в конце usingблока.

Это не обязательно означает, что это все еще не злоупотребление.

Роберт Харви
источник
4
Пока все очевидно. Вопрос в том, когда (если вообще) уместно ли реализовать IDisposable/ Dispose()несмотря на то, что не имеет ничего, что можно было бы распоряжаться в том смысле, который описывает OP?
2
Я думал, что ясно дал понять; есть не время , когда это уместно.
Роберт Харви
1
Суть в том, Disposeчто требуется для того, usingчтобы вообще иметь смысл (независимо от того, уместно ли это). Примеры OP все реализуют Dispose, не так ли? И IIUC, вопрос в том, правильно ли использовать эти классы Dispose, а не какой-то другой метод (например, Stop()для MiniProfiler).
1
Да, но это не вопрос. Вопрос в том, стоит ли это делать.
2
@delnan: Опять же, я думал, что ясно дал понять. Если это прояснит смысл вашего кода, тогда это хорошая идея. Если это не так, это не так.
Роберт Харви
1

usingбезопасен от ошибок и исключений. Это гарантирует Dispose(), что вызов будет вызван независимо от ошибок, которые делает программист. Это не мешает перехвату или обработке исключений, но Dispose()метод выполняется рекурсивно вверх по стеку, когда генерируется исключение.

Объекты, которые реализуют, IDisposeно им нечего распоряжаться по завершении. В лучшем случае, будущее проверяет их дизайн. Чтобы вам не приходилось реорганизовывать исходный код, тогда в будущем им необходимо что-то распоряжаться.

Reactgular
источник