¿Debería hacerse imposible llamar a IDisposable.Dispose () varias veces?

¿Deben las implementaciones de IDisposable hacer que Dispose () sea seguro para llamar varias veces? ¿O lo opuesto? ¿Qué enfoque toma la mayoría de las clases de .NET Framework?

Específicamente, ¿es seguro llamar a System.Data.Linq.DataContext.Dispose() varias veces?

La razón por la que pregunto es porque me pregunto si esta protección adicional es necesaria:

 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); } 

al desechar miembros IDisposable de una clase, o si simplemente debería llamar this.obj.Dispose() sin preocuparse por si se ha llamado previamente.

Debería estar seguro de llamarlo más de una vez, aunque probablemente debería evitarlo si puede.

Desde la página de MSDN en IDisposable.Dispose() :

Si el método Dispose de un objeto se llama más de una vez, el objeto debe ignorar todas las llamadas después de la primera. El objeto no debe lanzar una excepción si su método Dispose se llama varias veces.

Sí, sus implementaciones de IDisposable.Dispose () deben tolerar que se le llame varias veces. Después de la primera llamada a Dispose (), todas las demás llamadas pueden regresar inmediatamente.

Preferiría la primera parte de tu ejemplo de código para eliminar y anular las variables locales sobre la marcha.

Tenga en cuenta que su .Dispose () se puede invocar varias veces incluso si implementa los patrones Dispose y null en su código. Si varios consumidores tienen una referencia al mismo objeto desechable, el Dispose de ese objeto probablemente se invoque varias veces a medida que los consumidores abandonen sus referencias al mismo.

Los objetos deben ser tolerantes a que se llame a Dispose más de una vez, ya que, especialmente en presencia de excepciones, puede ser difícil para el código de limpieza saber con certeza qué cosas se han limpiado y cuáles no. Anular los campos identificables ID a medida que se limpian (y ser tolerantes con los que ya son nulos) hará que sea más fácil evitar llamadas redundantes a Dispose, pero realmente no cuesta nada hacer que los objetos toleren la eliminación múltiple, y ayuda a evitar el mal en algunas situaciones de lanzamiento de Excepción.

Si se tira un objeto, no debe tirarlo por segunda vez. Esto le ayuda a no prolongar la vida del objeto en el Recolector de basura.

Un patrón que uso normalmente es esto.

 // A base class that implements IDisposable. // By implementing IDisposable, you are announcing that // instances of this type allocate scarce resources. public class BaseClass: IDisposable { ///  /// A value indicating whether this instance of the given entity has /// been disposed. ///  ///  ///  if this instance has been disposed; otherwise, /// . ///  ///  /// 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. ///  private bool isDisposed; ///  /// Disposes the object and frees resources for the Garbage Collector. ///  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); } ///  /// Disposes the object and frees resources for the Garbage Collector. ///  /// If true, the object gets disposed. 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); } }