Должны ли реализации IDisposable.Dispose() быть идемпотентными?

Microsoft.NET Framework обеспечивает IDisposable интерфейс, который требует реализации void Dispose() метод. Его целью является включение ручного или объемного освобождения дорогостоящих ресурсов и IDisposable Реализация может быть выделено. Примеры включают базы данных, потоки и дескрипторы.

Мой вопрос заключается в том, следует ли Dispose() Метод быть идемпотентным - при вызове более одного раза в одном и том же экземпляре экземпляр должен быть "удален" только один раз, а последующие вызовы не вызывают исключений. В Java большинство объектов с похожим поведением (опять-таки, потоки и соединения с базами данных приходят мне на ум в качестве примеров) являются идемпотентными для их close() операция, которая оказывается аналогом для Dispose() метод.

Однако мой личный опыт работы с.NET (и, в частности, Windows Forms) показывает, что не все реализации (которые являются частью самой платформы.NET) являются идемпотентными, поэтому последующие вызовы этих ObjectDisposedException, Это действительно смущает меня тем, как следует подходить к реализации одноразового объекта. Есть ли общий ответ для сценария или он зависит от конкретного контекста объекта и его использования?

4 ответа

Решение

если реализация Dispose() метод быть идемпотентным

Да, так и должно быть. Невозможно сказать, сколько раз это будет называться.

От реализации метода утилизации на MSDN:

Метод Dispose должен вызываться несколько раз без исключения.

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

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

public void SomeMethod()
{
     if(_disposed)
     {
         throw new ObjectDisposedException();
     }
     else
     {
         // ...
     }

}

Из MSDN:

Разрешить вызов метода Dispose более одного раза без исключения. Метод ничего не должен делать после первого вызова.

Лично - да - я всегда делаю Dispose() идемпотентным.

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

Однако, в равной степени, в некоторых приложениях это может быть не так ясно.

Например, в сценарии декоратора: у меня может быть одноразовый объект A, украшенный другим одноразовым объектом B. Я могу захотеть явно удалить A, и, тем не менее, Dispose на B может также удалить экземпляр, который он упаковывает (think: streams).

Учитывая, что довольно легко заставить утилизировать идемпотента (то есть, если он уже утилизирован, ничего не делать), глупо не делать этого.

Другие вопросы по тегам