Является ли злоупотреблением использование IDisposable и использование в качестве средства для получения ограниченного поведения для безопасности исключений?

Что-то, что я часто использовал в C ++, позволяло классу A обрабатывать состояние входа и выхода для другого класса B через конструктор и деструктор A, чтобы убедиться, что если что-то в этой области выдает исключение, тогда B будет иметь известное состояние, когда область была закрыта. С точки зрения аббревиатуры, это не чистый RAII, но, тем не менее, это устоявшийся шаблон.

В C # я часто хочу сделать

class FrobbleManager
{
    ...

    private void FiddleTheFrobble()
    {
        this.Frobble.Unlock();
        Foo();                  // Can throw
        this.Frobble.Fiddle();  // Can throw
        Bar();                  // Can throw
        this.Frobble.Lock();
    }
}

Что нужно сделать вот так

private void FiddleTheFrobble()
{
    this.Frobble.Unlock();

    try
    {            
        Foo();                  // Can throw
        this.Frobble.Fiddle();  // Can throw
        Bar();                  // Can throw
    }
    finally
    {
        this.Frobble.Lock();
    }
}

если я хочу гарантировать состояние Frobble, когда FiddleTheFrobble вернется. Код будет лучше с

private void FiddleTheFrobble()
{
    using (var janitor = new FrobbleJanitor(this.Frobble))
    {            
        Foo();                  // Can throw
        this.Frobble.Fiddle();  // Can throw
        Bar();                  // Can throw
    }
}

где FrobbleJanitor выглядит примерно как

class FrobbleJanitor : IDisposable
{
    private Frobble frobble;

    public FrobbleJanitor(Frobble frobble)
    {
        this.frobble = frobble;
        this.frobble.Unlock();
    }

    public void Dispose()
    {
        this.frobble.Lock();
    }
}

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

Вопрос: Будет ли вышеизложенное считаться неправомерным использованием using и IDisposable?


person Johann Gerell    schedule 20.01.2010    source источник
comment
+1 только за имена классов и методов :-). Ну, честно говоря, и собственно вопрос.   -  person Joey    schedule 20.01.2010
comment
@Johannes: Да, я понимаю, почему - большинство людей просто ласкают свою скрипку, но я просто должен возразить против этого. ;-) И, кстати, +1 за схожесть вашего имени.   -  person Johann Gerell    schedule 20.01.2010
comment
Возможно, вы можете использовать область lock (this) {..} для достижения того же результата в этом примере?   -  person oɔɯǝɹ    schedule 20.01.2010
comment
@ oɔɯǝɹ: Полагаю, ты меня за ногу дёргал.   -  person Johann Gerell    schedule 21.01.2010
comment
не совсем, возможно, я недостаточно понимаю ваш сценарий. Кстати, TransactionScope позволяет использовать блок using () {..} для подобной очистки. Возможно, транзакция считается ресурсом, который нужно очистить, чтобы это считалось разрешенным.   -  person oɔɯǝɹ    schedule 21.01.2010
comment
@ oɔɯǝɹ: вы используете lock (), чтобы предотвратить одновременный доступ к чему-либо из разных потоков. Ничего общего с описываемым мной сценарием.   -  person Johann Gerell    schedule 25.01.2010
comment
IScopeable без Finalizer в следующей версии C # :)   -  person Jon Raynor    schedule 16.08.2013


Ответы (12)


Не думаю, что обязательно. IDisposable технически предназначен для использования для вещей, которые имеют неуправляемые ресурсы, но тогда директива using - это просто изящный способ реализации общего шаблона try .. finally { dispose }.

Пурист возразит: «Да, это оскорбительно», и в пуристическом смысле так оно и есть; но большинство из нас пишут код не с пуристской точки зрения, а с полухудожественной. На мой взгляд, использование конструкции «использование» таким образом действительно весьма артистично.

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

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

person Andras Zoltan    schedule 20.01.2010
comment
Я считаю, что это тоже художественное использование. Я думаю, что сообщение Самуала Джека со ссылкой на комментарий Эрика Ганнерсона подтверждает эту реализацию использования. Я вижу отличные замечания по этому поводу как от Андраса, так и от Эрика. - person IAbstract; 16.02.2010
comment
Некоторое время назад я говорил об этом с Эриком Ганнерсоном, и, по его словам, группа разработчиков C # намеревалась обозначить области действия с помощью. Фактически, он постулировал, что если бы они проектировались с использованием до оператора блокировки, не могло бы даже быть оператора блокировки. Это был бы блок использования с вызовом монитора или что-то в этом роде. ОБНОВЛЕНИЕ: только что понял, что следующий ответ от Эрика Липперта, который также входит в языковую команду. ;) Возможно, сама команда C # не полностью согласна с этим? Контекстом моего обсуждения с Ганнерсоном был класс TimedLock: bit.ly/lKugIO - person Haacked; 06.06.2011
comment
@Haacked - Забавно, не правда ли? ваш комментарий полностью подтверждает мою точку зрения; что вы поговорили с одним парнем в команде, и он был полностью за; затем, указывая на Эрика Липперта ниже, из той же команды не согласен. С моей точки зрения; C # предоставляет ключевое слово, которое использует интерфейс, а также дает красивый шаблон кода, который работает во многих сценариях. Если мы не должны злоупотреблять этим, то C # должен найти другой способ добиться этого. В любом случае, дизайнерам, вероятно, нужно поставить здесь своих уток подряд! А пока: using(Html.BeginForm()) сэр? не против, если я так сделаю, сэр! :) - person Andras Zoltan; 06.06.2011

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

Я считаю это злоупотреблением по трем причинам.

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

Во-вторых, потому что я ожидаю, что «использование» будет использоваться из вежливости, а не по необходимости. Причина, по которой вы используете "using" для удаления файла по завершении работы с ним, заключается не в том, что это необходимо, а в том, что это вежливо - кто-то еще может ждать, чтобы использовать этот файл, поэтому сказать «готово» - это морально правильный поступок. Я ожидаю, что я смогу провести рефакторинг «using», чтобы использованный ресурс удерживался дольше и утилизировался позже, и что единственное влияние этого - это небольшое неудобство для других процессов . Блок «using», который имеет семантическое влияние на состояние программы, является оскорбительным, потому что он скрывает важную, необходимую мутацию состояния программы в конструкции, которая выглядит так, как будто это сделано для удобства и вежливости, а не по необходимости.

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

Если бы вы принесли это на проверку кода в моем офисе, первый вопрос, который я бы задала, будет: «Действительно ли правильно блокировать блокировку при возникновении исключения?» Из вашей программы совершенно очевидно, что эта штука агрессивно повторно блокирует блокировку, что бы ни случилось. Верно? Возникло исключение. Программа находится в неизвестном состоянии. Мы не знаем, выбросили ли Foo, Fiddle или Bar, почему они выбросили или какие мутации они произвели в другом состоянии, которое не было очищено. Сможете ли вы убедить меня, что в этой ужасной ситуации всегда правильно сделать повторную блокировку?

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

Использование блока "using" для семантического эффекта делает этот фрагмент программы:

}

чрезвычайно значимый. Когда я смотрю на эту единственную закрывающую скобу, я не сразу думаю, что «эта скобка имеет побочные эффекты, которые имеют далеко идущие последствия для глобального состояния моей программы». Но когда вы так злоупотребляете «использованием», внезапно это происходит.

Второе, что я бы спросил, если бы увидел ваш исходный код: «Что произойдет, если после разблокировки, но до того, как будет введена попытка, будет выброшено исключение?» Если вы запускаете неоптимизированную сборку, компилятор мог вставить инструкцию no-op перед попыткой, и может произойти исключение прерывания потока на no-op. Это редко, но в реальной жизни случается, особенно на веб-серверах. В этом случае разблокировка происходит, но блокировки никогда не происходит, потому что исключение было сгенерировано до попытки. Вполне возможно, что этот код уязвим для этой проблемы и действительно должен быть написан

bool needsLock = false;
try
{
    // must be carefully written so that needsLock is set
    // if and only if the unlock happened:

    this.Frobble.AtomicUnlock(ref needsLock);
    blah blah blah
}
finally
{
    if (needsLock) this.Frobble.Lock();
}

Опять же, может быть, а может и нет, но я знаю, что нужно задать вопрос. С версией "using" он подвержен той же проблеме: исключение прерывания потока может быть сгенерировано после того, как Frobble заблокирован, но до того, как будет введена область с защитой от попытки, связанная с использованием. Но с версией "с использованием" я предполагаю, что это "ну и что?" ситуация. Очень жаль, если это произойдет, но я предполагаю, что «использование» используется только для вежливости, а не для изменения жизненно важного состояния программы. Я предполагаю, что если какое-то ужасное исключение прерывания потока произойдет точно в неподходящее время, тогда, ну что ж, сборщик мусора в конечном итоге очистит этот ресурс, запустив финализатор.

person Eric Lippert    schedule 20.01.2010
comment
Хотя я бы ответил на этот вопрос иначе, я думаю, что это хорошо аргументированный пост. Однако я не согласен с вашим утверждением, что using - это вопрос вежливости, а не корректности. Я считаю, что утечки ресурсов - это проблемы с правильностью, хотя часто они достаточно незначительны, чтобы не относиться к высокоприоритетным ошибкам. Таким образом, using блоки уже оказывают семантическое влияние на состояние программы, и они предотвращают не только неудобства для других процессов, но и, возможно, неисправную среду. Это не означает, что другой тип смыслового воздействия, подразумеваемый вопросом, также уместен. - person kvb; 20.01.2010
comment
Утечки ресурсов - это проблема корректности, да. Но отказ от использования не приводит к утечке ресурса; в конечном итоге ресурс будет очищен при запуске финализатора. Очистка может быть не такой агрессивной и своевременной, как хотелось бы, но она обязательно произойдет. - person Eric Lippert; 20.01.2010
comment
@Eric: Это действительно отличный ответ, который облекает слова и резюмирует все маленькие нытье, которые я упомянул о вопросе. И больше! - person Johann Gerell; 21.01.2010
comment
Замечательный пост, вот мои два цента :) Я подозреваю, что злоупотребление using связано с тем, что это аспектно-ориентированный ткач для бедняков на C #. Что действительно нужно разработчику, так это способ гарантировать, что какой-то общий код будет выполняться в конце блока без необходимости дублировать этот код. C # сегодня не поддерживает АОП (за исключением MarshalByRefObject и PostSharp). Однако преобразование компилятором оператора using позволяет достичь простого АОП путем изменения семантики детерминированного распределения ресурсов. Хотя это не очень хорошая практика, иногда приятно отказаться от нее ... - person LBushkin; 22.01.2010
comment
Хотя я в целом согласен с вашей позицией, бывают случаи, когда выгода от перенаправления using слишком привлекательна, чтобы отказываться от нее. Лучший пример, который я могу придумать, - это отслеживать и сообщать время выполнения кода: using( TimingTrace.Start("MyMethod") ) { /* code */ } Опять же, это АОП - Start() фиксирует время начала до начала блока, Dispose() фиксирует время окончания и регистрирует активность. Он не меняет состояние программы и не зависит от исключений. Он привлекателен еще и тем, что позволяет избежать использования сантехники и его частое использование в качестве шаблона смягчает запутанную семантику. - person LBushkin; 22.01.2010
comment
Я полностью согласен с Эриком. Можно резюмировать его ответ: не скрывайте магию! Будьте откровенны. - person bounav; 22.01.2010
comment
Magic не скрывается, если сделаны комментарии относительно неожиданного использования. Если реализация не предназначена для очистки ресурсов, прокомментируйте использование. Тем не менее, я бы не стал выбирать неизвестное состояние программы или добровольно допускать утечку ресурсов, независимо от того, насколько незначительной может казаться эта утечка. - person IAbstract; 16.02.2010
comment
Забавно, я всегда думал об использовании как о средстве поддержки RAII. Мне кажется довольно интуитивно понятным рассматривать блокировку как тип ресурса. - person Brian; 17.02.2010
comment
@ Брайан: Блокировка может быть ресурсом. Мьютекс - это дескриптор операционной системы, а это дефицитный глобальный ресурс. Но есть большая разница между ресурсом, который должен выпускаться своевременно, и семантически важной мутацией, которая должна происходят в определенное время, чтобы поддерживать логический инвариант программы. - person Eric Lippert; 17.02.2010
comment
... и битва продолжается !! stackoverflow.com/questions/2725449/ - person Paul Kohler; 29.04.2010
comment
Я ожидаю, что using будет использоваться из вежливости, а не из-за необходимости - заставить веб-приложение исчерпать пул соединений с БД под нагрузкой из-за неспособности детерминированно закрыть соединения - это не просто недостаток вежливости. - person Joe; 30.05.2013

Если вам просто нужен чистый код с ограниченной областью видимости, вы также можете использовать лямбды, á la

myFribble.SafeExecute(() =>
    {
        myFribble.DangerDanger();
        myFribble.LiveOnTheEdge();
    });

где метод .SafeExecute(Action fribbleAction) оборачивает блок try - catch - finally.

person herzmeister    schedule 20.01.2010
comment
В этом примере вы пропустили входное состояние. Кроме того, убедитесь, что вы обернули try / finally, но вы ничего не вызываете в finally, поэтому, если что-то в лямбде выдает, вы не знаете, в каком состоянии вы находитесь. - person Johann Gerell; 20.01.2010
comment
Это была просто идея, я подумал, может, ты сможешь адаптировать ее под свои нужды. У метода могут быть дополнительные параметры, для него может потребоваться дополнительный делегат для блока finally, вы также можете использовать более изящный делегат, чем простое действие, и т. Д. И т. Д. - person herzmeister; 20.01.2010
comment
Ах! Хорошо, я полагаю, вы имеете в виду, что SafeExecute - это метод Fribble, который вызывает Unlock() и Lock() в завернутом try / finally. Приношу свои извинения. Я сразу увидел SafeExecute как общий метод расширения и поэтому упомянул об отсутствии состояния входа и выхода. Виноват. - person Johann Gerell; 20.01.2010
comment
Его можно смоделировать как метод экземпляра или как общий метод расширения служебной программы, в зависимости от ваших потребностей. - person herzmeister; 20.01.2010
comment
Будьте очень осторожны с этим прибором. В случае захваченных местных жителей могут быть некоторые опасные незаметные непреднамеренные продления жизни! - person jason; 20.01.2010
comment
Юк. Зачем прятать try / catch / finally? Делает ваш код скрытым, чтобы его могли прочитать другие. - person Frank Schwieterman; 20.01.2010
comment
@Jason: Вместо этого можно использовать метод myFribble.SafeExecute (Action ‹TDangerousFrobble› frobbleAction). Экземпляр DangerousFrobble может быть частным для класса Fribble, или DangerousFrobble может быть объявлен как частный внутренний класс Fribble в целом. Затем экземпляр DangerousFrobble будет доступен вызывающей стороне только в рамках этого единственного метода. - person herzmeister; 21.01.2010
comment
@Yukky Frank: Это была не моя идея что-либо скрывать, это была просьба спрашивающего. :-P ... Тем не менее, просьба, наконец, заключается в том, чтобы не повторяться. У вас может быть много методов, для которых требуется один и тот же шаблонный фрейм для чистого получения / выпуска чего-либо, и вы не хотите навязывать это вызывающему (подумайте об инкапсуляции). Также можно было бы более четко называть такие методы, как .SafeExecute (...), чтобы достаточно хорошо сообщать о том, что они делают. - person herzmeister; 21.01.2010
comment
@herzmeister der welten: Это никуда не годится. Дело в том, что с анонимными делегатами или лямбда-выражениями (очень частный случай анонимных делегатов) внешние переменные захватываются в анонимный класс, который инкапсулирует внешние переменные и анонимный делегат. В зависимости от того, что происходит с экземплярами этого анонимного класса, время жизни внешних переменных может быть увеличено непреднамеренно. - person jason; 21.01.2010
comment
@Jason: Насколько я помню, фиксируются только те внешние переменные, которые вы действительно используете. Мы могли бы проверить это с помощью отражателя, переключив такой фрагмент кода с .NET 3.5 обратно на 2.0. Поэтому я предлагаю не использовать экземпляр myFribble внутри анонимного делегата, а объявить DangerousFrobble, который инкапсулирует вызовы опасных методов, чтобы вызывающий должен был сделать myFribble.SafeExecute(dangerousFrobble => dangerousFrobble.PushRedButton()); - person herzmeister; 24.01.2010

Эрик Ганнерсон, работавший в группе разработчиков языка C #, дал этот ответ почти на тот же вопрос:

Дуг спрашивает:

re: Оператор блокировки с тайм-аутом ...

Я проделывал этот трюк раньше, чтобы иметь дело с общими шаблонами в различных методах. Обычно получение блокировки, но есть и другие. Проблема в том, что это всегда похоже на взлом, поскольку объект на самом деле не одноразовый, а скорее «call-back-at-the-end-of-a-scope -able».

Дуг,

Когда мы решили [sic] использовать оператор, мы решили назвать его «использование», а не что-то более конкретное для удаления объектов, чтобы его можно было использовать именно для этого сценария.

person Samuel Jack    schedule 20.01.2010
comment
Эта цитата должна сказать, что он имел в виду, когда говорил именно об этом сценарии, поскольку я сомневаюсь, что он имел в виду этот будущий вопрос. - person Frank Schwieterman; 20.01.2010
comment
@Frank Schwieterman: Я завершил цитату. Судя по всему, люди из команды C # действительно думали, что функция using не ограничивается удалением ресурсов. - person paercebal; 15.07.2010

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

void UseMeUnlocked(Action callback) {
  Unlock();
  try {
    callback();
  }
  finally {
    Lock();
  }
}

Но без ламдов это становится немного неловко. Тем не менее, я использовал IDisposable, как и вы.

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

  • Ничего не делать, исключение не подлежит восстановлению. Нормальный случай. Вызов разблокировки не имеет значения.
  • Поймать и обработать исключение
  • Восстановите состояние в его коде и позвольте исключению пройти по цепочке вызовов.

Последние два требуют, чтобы вызывающий объект явно написал блок попытки. Теперь мешает инструкция using. Это вполне может ввести клиента в кому, что заставит его поверить, что ваш класс заботится о состоянии и не требует дополнительной работы. Это почти никогда не бывает точным.

person Hans Passant    schedule 20.01.2010
comment
Я думаю, что принудительный случай был приведен выше herzmeister der welten, даже если OP, похоже, не понравился пример. - person Benjamin Podszun; 20.01.2010
comment
Да, я интерпретировал его SafeExecute как общий метод расширения. Теперь я понимаю, что это, вероятно, имелось в виду как Fribble метод, который вызывает Unlock() и Lock() в завернутом try / finally. Виноват. - person Johann Gerell; 20.01.2010
comment
Игнорирование исключения не означает, что оно не подлежит восстановлению. Это означает, что он будет обработан выше в стеке. Например, исключения можно использовать для корректного выхода из потока. В этом случае вы должны правильно освободить свои ресурсы, в том числе заблокированные блокировки. - person paercebal; 27.08.2010

Примером из реальной жизни является BeginForm ASP.net MVC. В основном вы можете написать:

Html.BeginForm(...);
Html.TextBox(...);
Html.EndForm();

or

using(Html.BeginForm(...)){
    Html.TextBox(...);
}

Html.EndForm вызывает Dispose, а Dispose просто выводит тег </form>. Хорошая вещь в этом заключается в том, что скобки {} создают видимую «область видимости», которая упрощает просмотр того, что находится внутри формы, а что нет.

Я бы не стал злоупотреблять им, но, по сути, IDisposable - это просто способ сказать: «Вы ДОЛЖНЫ вызвать эту функцию, когда закончите с ней». MvcForm использует его, чтобы убедиться, что форма закрыта, Stream использует его, чтобы убедиться, что поток закрыт, вы можете использовать его, чтобы убедиться, что объект разблокирован.

Лично я бы использовал его только в том случае, если следующие два правила верны, но они установлены мной произвольно:

  • Dispose должен быть функцией, которая должна выполняться всегда, поэтому не должно быть никаких условий, кроме Null-Checks.
  • После Dispose () объект нельзя использовать повторно. Если мне нужен объект многократного использования, я бы предпочел дать ему методы открытия / закрытия, а не уничтожать. Поэтому я бросаю InvalidOperationException при попытке использовать удаленный объект.

В конце концов, все дело в ожиданиях. Если объект реализует IDisposable, я предполагаю, что ему нужно выполнить некоторую очистку, поэтому я называю это. Я думаю, что это обычно лучше, чем наличие функции «Завершение работы».

При этом мне не нравится эта строка:

this.Frobble.Fiddle();

Поскольку FrobbleJanitor теперь «владеет» Frobble, я задаюсь вопросом, не лучше ли было бы вместо этого вызвать Fiddle на Frobble в Janitor?

person Michael Stum    schedule 20.01.2010
comment
Что касается вашего, мне не нравится эта строка - я на самом деле вкратце подумал о том, чтобы сделать это таким образом, когда формулировал вопрос, но я не хотел загромождать семантику моего вопроса. Но я вроде как согласен с вами. Вроде. :) - person Johann Gerell; 21.01.2010
comment
Проголосовали за предоставленный пример от самой Microsoft. Обратите внимание, что в случае, когда вы упоминаете, должно быть создано особое исключение: ObjectDisposedException. - person Antitoon; 16.02.2019

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

person Simon Steele    schedule 20.01.2010

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

Вообще мне нравится синтаксис, он убирает много лишнего из реального мяса. Пожалуйста, подумайте о том, чтобы дать вспомогательному классу хорошее имя - возможно ... Scope, как в примере выше. Название должно указывать на то, что оно инкапсулирует фрагмент кода. * Scope, * Block или что-то подобное должно это делать.

person Benjamin Podszun    schedule 20.01.2010
comment
«Дворник» взят из старой статьи на C ++ Андрея Александреску об охране прицела, еще до того, как ScopeGuard попал в Loki. - person Johann Gerell; 20.01.2010
comment
@Benjamin: Мне нравится класс TransactionScope, я о нем раньше не слышал. Может быть, потому что я нахожусь в стране .Net CF, где это не входит ... :-( - person Johann Gerell; 20.01.2010

Примечание. Моя точка зрения, вероятно, смещена из-за моего опыта работы с C ++, поэтому значение моего ответа следует оценивать с учетом этой возможной предвзятости ...

Что говорит спецификация языка C #?

Цитата из спецификации языка C #:

8.13 Оператор using

[...]

Ресурс - это класс или структура, реализующая System.IDisposable, которая включает единственный метод без параметров с именем Dispose. Код, использующий ресурс, может вызвать Dispose, чтобы указать, что ресурс больше не нужен. Если Dispose не вызывается, автоматическое удаление в конечном итоге происходит как следствие сборки мусора.

Код, использующий ресурс - это, конечно, код, начинающийся с ключевого слова using и идущий до области, прикрепленной к using.

Думаю, это нормально, потому что Lock - это ресурс.

Возможно, ключевое слово using было выбрано неправильно. Возможно, его следовало назвать scoped.

Тогда мы можем рассматривать почти все как ресурс. Дескриптор файла. Сетевое подключение ... Нить?

Нить???

Используете (или злоупотребляете) ключевым словом using?

Было бы блестящим, если бы (ab) использовал ключевое слово using, чтобы убедиться, что работа потока завершена перед выходом из области видимости?

Херб Саттер, кажется, думает, что это блестяще, поскольку он предлагает интересное использование шаблона IDispose для ожидания завершения работы потока:

http://www.drdobbs.com/go-parallel/article/showArticle.jhtml?articleID=225700095.

Вот код, скопированный из статьи:

// C# example
using( Active a = new Active() ) {    // creates private thread
       …
       a.SomeWork();                  // enqueues work
       …
       a.MoreWork();                   // enqueues work
       …
} // waits for work to complete and joins with private thread

Хотя код C # для активного объекта не предоставляется, написано, что C # использует шаблон IDispose для кода, версия C ++ которого содержится в деструкторе. И, глядя на версию C ++, мы видим деструктор, который ожидает завершения внутреннего потока перед завершением, как показано в этом другом отрывке из статьи:

~Active() {
    // etc.
    thd->join();
 }

Так что, что касается Херба, он блестящий.

person paercebal    schedule 27.08.2010

Я считаю, что ответ на ваш вопрос отрицательный, это не будет злоупотреблением IDisposable.

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

Поскольку вы создаете new FrobbleJanitor явно каждый раз, когда переходите к оператору using, вы никогда не используете один и тот же объект FrobbeJanitor дважды. И поскольку его цель - управлять другим объектом, Dispose кажется подходящим для задачи по освобождению этого («управляемого») ресурса.

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

Единственное, о чем я лично беспокоюсь, это то, что менее ясно, что происходит с using (var janitor = new FrobbleJanitor()), чем с более явным блоком try..finally, где операции Lock & Unlock видны напрямую. Но выбор подхода, вероятно, зависит от личных предпочтений.

person stakx - no longer contributing    schedule 20.01.2010

Я думаю, ты все сделал правильно. Перегрузка Dispose () была бы проблемой, когда тот же класс позже провел очистку, которую он действительно должен был выполнить, и время жизни этой очистки изменилось, чтобы отличаться от времени, когда вы ожидаете удерживать блокировку. Но поскольку вы создали отдельный класс (FrobbleJanitor), который отвечает только за блокировку и разблокировку Frobble, вещи достаточно разделены, вы не столкнетесь с этой проблемой.

Я бы переименовал FrobbleJanitor, вероятно, во что-то вроде FrobbleLockSession.

person Frank Schwieterman    schedule 20.01.2010
comment
Насчет переименования - Я использовал Дворник по той причине, что разобрался с Блокировкой / Разблокировкой. Я думал, что это рифмуется с другими вариантами имени. ;-) Если бы я использовал этот метод в качестве образца для всей моей базы кода, я бы определенно включил что-то более общее описательное, как вы подразумевали в Session. Если бы это были старые времена C ++, я бы, вероятно, использовал FrobbleUnlockScope. - person Johann Gerell; 21.01.2010

Это не оскорбление. Вы используете их для того, для чего они созданы. Но вам, возможно, придется рассматривать друг друга в соответствии с вашими потребностями. Например, если вы выбираете «артистизм», вы можете использовать «using», но если ваш фрагмент кода выполняется много раз, то по соображениям производительности вы можете использовать «попробовать» .. «наконец» конструкции, потому что «использование» обычно включает создание объекта.

person ata    schedule 20.01.2010