Должен ли IDisposable.Dispose() быть безопасным звонить несколько раз?
Если реализации IDisposable делают Dispose() безопасным для вызова несколько раз? Или наоборот? Какой подход подходит для большинства классов .NET Framework?
В частности, безопасно ли вызывать System.Data.Linq.DataContext.Dispose()
несколько раз?
Я прошу, потому что мне интересно, нужна ли эта дополнительная защита:
public override void Dispose(bool disposing)
{
// Extra protection...
if (this.obj != null)
{
this.obj.Dispose();
this.obj = null;
}
// Versus simply...
this.obj.Dispose();
base.Dispose(disposing);
}
при удалении IDisposable членов класса или я должен просто вызвать this.obj.Dispose()
, не заботясь о том, был ли он ранее вызван.
Ответы
Ответ 1
Вы должны быть в безопасности, чтобы называть его более одного раза, хотя вам, вероятно, следует избегать его, если сможете.
На странице MSDN IDisposable.Dispose()
:
Если объект Dispose method вызывается более одного раза, объект должен игнорировать все вызовы после первого. Объект не должен генерировать исключение, если его метод Dispose вызывается несколько раз.
Ответ 2
Да, ваши реализации IDisposable.Dispose() должны допускать многократные вызовы. После первого вызова Dispose() все остальные вызовы могут сразу вернуться.
Я бы предпочел первую часть вашего примера кода, чтобы уничтожить и обнулить локальные переменные во время вашей работы.
Помните, что ваш .Dispose() может вызываться несколько раз, даже если вы реализуете Dispose и null шаблоны в вашем коде. Если несколько потребителей имеют ссылку на один и тот же одноразовый объект, то этот объект Dispose, вероятно, будет вызываться несколько раз, поскольку эти потребители отбрасывают свои ссылки на него.
Ответ 3
Объекты должны быть толерантными к тому, что Dispose вызывается более одного раза, поскольку - особенно при наличии исключений - может быть сложно, чтобы код очистки знал наверняка, какие вещи были очищены, а какие нет. Отбрасывание полей IDisposable при их очистке (и толерантность к тем, которые уже имеют значение null) упростит удаление избыточных вызовов Dispose, но на самом деле ничего не стоит, чтобы объекты допускали множественное удаление, и это помогает избегайте неприятностей в некоторых ситуациях броска исключений.
Ответ 4
Если объект размещен, вы не должны избавляться от него второй раз. Это помогает вам не продлевать жизнь объекта в сборщике мусора.
Обычно я использую шаблон.
// A base class that implements IDisposable.
// By implementing IDisposable, you are announcing that
// instances of this type allocate scarce resources.
public class BaseClass: IDisposable
{
/// <summary>
/// A value indicating whether this instance of the given entity has
/// been disposed.
/// </summary>
/// <value>
/// <see langword="true"/> if this instance has been disposed; otherwise,
/// <see langword="false"/>.
/// </value>
/// <remarks>
/// If the entity is disposed, it must not be disposed a second
/// time. The isDisposed field is set the first time the entity
/// is disposed. If the isDisposed field is true, then the Dispose()
/// method will not dispose again. This help not to prolong the entity's
/// life in the Garbage Collector.
/// </remarks>
private bool isDisposed;
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.
/// </summary>
public void Dispose()
{
this.Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.
/// </summary>
/// <param name="disposing">If true, the object gets disposed.</param>
protected virtual void Dispose(bool disposing)
{
if (this.isDisposed)
{
return;
}
if (disposing)
{
// Dispose of any managed resources here.
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// Note disposing is done.
this.isDisposed = true;
}
// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~BaseClass()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
}