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

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

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

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


person Ivaylo Slavov    schedule 19.01.2012    source источник


Ответы (4)


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

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

Из Реализация метода Dispose в MSDN:

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

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

person Oded    schedule 19.01.2012
comment
Тот факт, что Microsoft не всегда следует своим собственным рекомендациям, не означает, что вы должны это делать. - person linkerro; 19.01.2012
comment
@linkerro - Не могли бы вы расширить свой довольно загадочный комментарий? - person Oded; 19.01.2012
comment
Интересно, имеет ли он в виду то, что WinForms выдает исключение, если вы дважды удаляете элемент управления. - person DaveShaw; 19.01.2012
comment
@DaveShaw, вот почему я задал этот вопрос. Одед, спасибо за ответ, он подтвердил мою веру в то, что идемпотентная логика утилизации необходима. Тем не менее, у меня возникает другой вопрос - когда также следует применять механизмы синхронизации? - person Ivaylo Slavov; 19.01.2012
comment
@IvayloSlavov - Всякий раз, когда вы делите состояние между потоками ... Но вам действительно следует задать для этого новый вопрос. - person Oded; 19.01.2012
comment
Еще раз спасибо, обязательно сделаю. - person Ivaylo Slavov; 19.01.2012
comment
Как сказал Дэйв, я говорю о том, что winforms обрабатывает освобождение, в частности, и обрабатывает освобождение в целом, что подвержено ошибкам. - person linkerro; 20.01.2012

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

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

}
person Emond Erno    schedule 19.01.2012
comment
Чтобы справиться здесь, можно просто выдать исключение. И я не совсем уверен, нужно ли вообще что-то требовать. Пожалуйста, не требуйте, чтобы удаленные объекты продолжали каким-то образом работать. Попытка использовать удаленный объект является ошибкой и должна рассматриваться как таковая. - person R. Martinho Fernandes; 19.01.2012
comment
@R.MartinhoFernandes - да, но еще есть что добавить :) - person Emond Erno; 19.01.2012
comment
@R.MartinhoFernandes, я думаю, что @Emo говорит о том, что если у вас все еще есть ссылка на уже удаленный объект и вы вызываете некоторые другие его методы, эти методы должны генерировать ObjectDisposedException или иным образом уведомлять вызывающую сторону о том, что объект операция недопустима для этого штата. К сожалению, я полагаю, что это может быть достигнуто ценой осведомленности о синхронизации. - person Ivaylo Slavov; 19.01.2012
comment
Я согласен с тем, что выбрасывать глупое исключение (используя таксономию Эрика Липперта) - это правильно. И теперь, когда ответ проясняет ситуацию, я даже поставил +1. Как это было раньше, это могло быть неверно истолковано как означающее что-то другое. - person R. Martinho Fernandes; 19.01.2012
comment
@R.MartinhoFernandes - Примечание для себя: никогда не пытайтесь объяснить что-то в одной строке, если две строки лучше. - person Emond Erno; 19.01.2012

Из MSDN:

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

person Thomas Levesque    schedule 19.01.2012
comment
Возможно, вам следовало включить следующий пункт для ясности: выбрасывать исключение ObjectDisposedException из методов экземпляра для этого типа (кроме Dispose), когда ресурсы уже удалены. Это правило не применяется к методу Dispose, так как его можно вызывать несколько раз без создания исключения. - person Jamiec; 19.01.2012

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

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

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

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

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

person Rob Levine    schedule 19.01.2012