Действительно ли это оскорбительно, чтобы использовать 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?

107
задан Johann Gerell 20 January 2010 в 13:13
поделиться

11 ответов

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

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

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

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

32
ответ дан 24 November 2019 в 03:41
поделиться

Это следует заметить, если вы новичок в java, каждый объект унаследовал от объекта

защищенный собственный объект clone () создает CloneNotSupportedException;

-121--647380-

Фил прав - пароль не дает взглянуть на модули, они не зашифрованы сами. Я знаю, что в Excel 2007 файл, по сути, представляет собой zipped коллекцию XML и других файлов, но я не знаю, как обрабатывается шифрование. Для более ранних версий - Excel 2, 3, 4, 5, 95, 97, 2000, XP, & 2003, существует полная документация OpenOffice.org формата файлов Microsoft Excel :

Формат файлов Excel называется BIFF (двоичный формат файла обмена). Он используется для хранения всех типов документов: документов рабочих таблиц, документов рабочих книг и документов рабочей области. Существуют различные версии этого формата файла в зависимости от версии Excel, написавшей файл, и в зависимости от типа документа.

Документ рабочей книги с несколькими листами (BIFF5-BIFF8) обычно хранится с использованием составного формата файла документа (также известного как «формат файла OLE2 места хранения» или «формат файла, совместимый с Microsoft Office место хранения»). Он содержит несколько потоков для различных типов данных. Полную документацию по формату составных файлов документов можно найти здесь .

Блок защиты рабочей книги появляется сразу после блока DEFINEDNAME (т. е. именованных диапазонов) в большинстве потоков BIFF, хотя BIFF8 является существенным исходный от этого образца. Блок защиты записей In Biff5 - Biff8 структуру блока защиты рабочей книги:

  • параметры настройки окна WINDOWPROTECT: 1 = protected
  • PROTECT Содержимое рабочей книги: 1 = protected
  • ПАРОЛЬ Хэш-значение пароля; 0 = нет пароля
  • PROT4REV Общая книга: 1 = защищено
  • PROT4REVPASS Хэш-значение общего пароля; 0 = без пароля

В блоке паролей хранится 16-битное значение хэша, рассчитанное на основе пароля защиты листа или книги.

-121--3604294-

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

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

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

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

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

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

Может быть, это так, может это не так. Я хочу сказать, что с кодом, как он был первоначально написан, рецензент кода знает, чтобы задать вопрос . С кодом, который использует «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, но до ввода защищенной области, связанной с использованием. Но с версией "using" я предполагаю, что это ситуация "so what?". Жаль, если это произойдет, но я предполагаю, что "использование" - это только быть вежливым, а не мутировать жизненно важное состояние программы. Я предполагаю, что если какое-то ужасное исключение прерывания потока произойдет точно не в то время, тогда сборщик мусора очистит этот ресурс, запустив финализатор.

69
ответ дан 24 November 2019 в 03:41
поделиться

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

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

, но это имеет тенденцию получить немного неловко без ламды. Тем не менее, я использовал IDSposable, как ты.

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

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

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

11
ответ дан 24 November 2019 в 03:41
поделиться

Если вы просто хотите чистый, определенный навес, вы также можете использовать Lambdas, à la

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

, где .safeexecute (Action Fribblauction) метод включает - Уловить - Наконец блок.

27
ответ дан 24 November 2019 в 03:41
поделиться

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

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

Re: reap of Timeout ...

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

Doug,

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

24
ответ дан 24 November 2019 в 03:41
поделиться

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

4
ответ дан 24 November 2019 в 03:41
поделиться

Я считаю, что ответ на ваш вопрос нет, это не было бы злоупотреблением IDSposable .

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

Так как вы создаете New FrobbleNistritor , явно каждый раз, когда вы попадаете в , используя , вы никогда не используете один и тот же Frobbeganitor объект дважды. И поскольку это цель - управлять другим объектом, Утилизация , кажется, подходит для задачи освобождения этого («управляемого» ресурса.

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

Только то, где я лично переживаю, - это менее ясно, что происходит с с использованием , используя (var deanitor = new frobblenistritor ()) , чем с более явным .. Фарчик Блокировка & Разблокировка Операции непосредственно видны непосредственно. Но какой подход принимается, вероятно, сводится к вопросу личных предпочтений.

3
ответ дан 24 November 2019 в 03:41
поделиться

Это не оскорбительно. Вы используете их, для чего они созданы. Но вам, возможно, придется рассмотреть один по другому в соответствии с вашими потребностями. Для E.G, если вы выбираете «Artistry», то вы можете использовать «Использование», но если ваш кусок кода выполнен много раз, то по причинам производительности вы можете использовать «попробуйте» .. Потому что «использование» обычно включает создания объекта.

1
ответ дан 24 November 2019 в 03:41
поделиться

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

Я бы переименул Frobblenitoritor, что, вероятно, к чему-то вроде Frobblelocsionsessionsession.

1
ответ дан 24 November 2019 в 03:41
поделиться

Шимин в этом: Я согласен с большей частью здесь, что это хрупко, но полезно. Я хотел бы указать вам в System.transaction.transactionCope класс, который делает что-то вроде вы хотите сделать.

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

4
ответ дан 24 November 2019 в 03:41
поделиться

Реальным примером является BeginForm ASP.net MVC. Обычно вы можете написать:

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

или

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

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

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

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

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

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

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

this.Frobble.Fiddle();

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

7
ответ дан 24 November 2019 в 03:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: