Должен ли IDisposable.Dispose () быть безопасным для вызова несколько раз? - PullRequest
38 голосов
/ 15 марта 2011

Должны ли реализации 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(), не заботясь о том, был ли он вызван ранее.

Ответы [ 4 ]

54 голосов
/ 15 марта 2011

Вы можете звонить по телефону несколько раз, хотя, вероятно, следует избегать его, если можете.

Со страницы MSDN на IDisposable.Dispose():

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

6 голосов
/ 15 марта 2011

Да, ваши реализации IDisposable.Dispose () должны допускать вызовы несколько раз. После первого вызова Dispose () все остальные вызовы могут сразу же вернуться.

Я бы предпочел, чтобы первая часть вашего примера кода располагала и обнуляла локальные переменные на ходу.

Помните, что ваш .Dispose () может вызываться несколько раз, даже если вы внедрили в свой код шаблоны Dispose и null. Если несколько потребителей содержат ссылку на один и тот же одноразовый объект, то Dispose этого объекта, вероятно, будет вызываться несколько раз, поскольку эти потребители отбрасывают свои ссылки на него.

2 голосов
/ 15 марта 2011

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

0 голосов
/ 15 марта 2011

Если объект утилизирован, вы не должны его утилизировать во второй раз. Это помогает вам не продлевать срок службы объекта в сборщике мусора.

Шаблон, который я обычно использую, таков.

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