Чи підходить "використання" в контексті, коли немає чим розпоряджатися?


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 уникав другого підходу. Але другого слід уникати у всіх випадках, оскільки важко писати та підтримувати.
Арсеній Муренко



Відповіді:


7

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

Основне використання цього інтерфейсу - це звільнення некерованих ресурсів.

Отже, якщо ваш клас використовує будь-які некеровані ресурси, не має значення, коли ви можете чи не хочете, щоб це сталося з GC - це не має нічого спільного з GC, оскільки некеровані ресурси не є GC'ed ( https: // stackoverflow. com / questions / 3607213 / що-значить, під керуванням-vs-unmanaged-resources-in net ).

Таким чином, мета "використання" - це не уникнути очікування на GC, це змусити випустити ці некеровані ресурси зараз , перш ніж екземпляр класу вийде із сфери застосування, і це буде викликано Finalize. Це важливо з причин, які повинні бути очевидними - некерований ресурс може мати залежність від інших некерованих ресурсів, і якщо вони утилізуються (або допрацьовуються) у неправильному порядку, можуть трапитися погані речі.

Отже, якщо клас, який встановлюється у блоці, що використовує, використовує будь-які некеровані ресурси, то тоді відповідь - так.

Зауважте, що IDisposable не є розпоряджувальним щодо того, що він використовується лише для звільнення некерованих ресурсів, однак - лише це головна мета. Це може бути так , що автор класу мають деякі дії , які вони хочуть провести в життя відбувається в певний час, і реалізації IDisposable може бути способом отримання , що має статися, але дійсно Чи , що елегантне рішення є те , що може відповідати лише у кожному конкретному випадку.

Незалежно від того, що використовувати "using", означає, що клас реалізує IDisposable, тому він не порушує виразності коду; це дає зрозуміти, що відбувається насправді.


"Основне використання цього інтерфейсу - це звільнення некерованих ресурсів." Я кажу, що документація Майкрософт підходить до цього питання. Можливо, саме так вони використовують його у 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()незважаючи на те, що не має нічого розпоряджатися в тому сенсі, що описує ОП?

2
Я думав, що я це зрозумів; є НЕ час , коли це доречно.
Роберт Харві

1
Моя думка, Disposeпотрібна для того, usingщоб мати сенс взагалі (незалежно від того, підходить це). Всі приклади ОП реалізують Dispose, чи не так? І IIUC, питання полягає в тому, чи правильно використовувати ці класи Dispose, а не якийсь інший метод (наприклад, Stop()для MiniProfiler).

1
Так, але це не питання. Питання полягає в тому, чи варто це робити.

2
@delnan: Знову ж таки, я думав, що це я зрозумів. Якщо це зробить наміри вашого коду яснішими, то це гарна ідея. Якщо це не так, це не так.
Роберт Харві

1

usingє безпечним для помилок та винятків. Це гарантує, що Dispose()буде називатися без уваги будь-яких помилок, які робить програміст. Це не перешкоджає вибору або обробці винятків, але Dispose()метод виконується рекурсивно вгору стека, коли викидається виняток.

Об'єкти, які реалізують, IDisposeале не мають чим розпоряджатися, коли вони зроблені. У кращому випадку майбутні підтверджують свій дизайн. Так що вам не доведеться перефактурувати свій вихідний код, коли в майбутньому їм потрібно щось утилізувати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.