Должны ли idisposable.Dispose() реализации быть идемпотентными?
Структура Microsoft.NET предоставляет интерфейс IDisposable
, который требует реализации метода void Dispose()
. Его цель состоит в том, чтобы позволить вручную или освобождать дорогостоящие ресурсы на основе возможностей, которые может быть реализована IDisposable
. Примеры включают базы данных, потоки и дескрипторы.
Мой вопрос заключается в том, должна ли реализация метода Dispose()
быть идемпотентной - при вызове более одного раза в одном экземпляре экземпляр "удаляется" только один раз, а последующие вызовы не бросать исключения. В Java большинство объектов, которые имеют подобное поведение (опять-таки потоки и соединения с базой данных, которые приходят мне на ум в качестве примеров), являются идемпотентными для их операции close()
, которая является аналогом метода Dispose()
.
Однако мой личный опыт работы с .NET(и Windows Forms в частности) показывает, что не все реализации (которые являются частью самой платформы .NET) являются идемпотентными, поэтому последующие вызовы для них вызывают ObjectDisposedException
. Это действительно смущает меня в отношении того, как следует подходить к реализации одноразового объекта. Существует ли общий ответ для сценария или зависит от конкретного контекста объекта и его использования?
Ответы
Ответ 1
если реализация метода Dispose()
будет идемпотентной
Да, должно. Неизвестно, сколько раз он будет называться.
От Внедрение метода удаления в MSDN:
a Метод Dispose должен быть вызван несколько раз, не вызывая исключения.
Объект с хорошей реализацией IDispose
будет иметь флаг логического поля, указывающий, был ли он уже удален и последующие вызовы ничего не делают (поскольку он уже был удален).
Ответ 2
Да, также убедитесь, что другие методы класса отвечают правильно, когда они вызывается, когда объект уже был удален.
public void SomeMethod()
{
if(_disposed)
{
throw new ObjectDisposedException();
}
else
{
// ...
}
}
Ответ 3
Из MSDN:
Разрешить метод Dispose вызываться более одного раза без исключения исключения. После первого вызова метод ничего не должен делать.
Ответ 4
Лично - Да - я всегда делаю Dispose() idempotent.
Во время обычного жизненного стиля объекта в данном приложении оно может и не понадобиться - жизненный образ от создания к удалению может быть детерминированным и хорошо известным.
Однако одинаково, в некоторых приложениях это может быть не так ясно.
Например, в сценарии декоратора: у меня может быть одноразовый объект A, украшенный другим одноразовым объектом B. Возможно, я хочу явно распорядиться A, и все же Dispose on B также может удалить экземпляр, который он обертывает (think: streams).
Учитывая, что относительно легко сделать Dispose idempotent (т.е. если он уже установлен, ничего не делать), кажется глупым не делать.