Подтвердить что ты не робот

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

4b9b3361

Ответ 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);
    }      
}