IDisposable.Dispose()可以安全地多次调用吗?

时间:2011-03-15 02:26:55

标签: c# .net idisposable

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 个答案:

答案 0 :(得分:54)

你应该可以安全地多次调用它,但如果可以,你应该尽量避免使用它。

来自IDisposable.Dispose()上的MSDN页面:

  

如果多次调用对象的Dispose方法,则该对象必须忽略第一个之后的所有调用。如果多次调用Dispose方法,则该对象不得抛出异常。

答案 1 :(得分:6)

是的,您的IDisposable.Dispose()实现应该容忍被多次调用。在第一次调用Dispose()之后,所有其他调用都可以立即返回。

我更喜欢你的代码示例的第一部分,在你去的时候处理和归零局部变量。

请注意,即使在代码中实现Dispose和null模式,也可能多次调用.Dispose()。如果多个使用者持有对同一个一次性对象的引用,则该对象的Dispose可能会被多次调用,因为这些对象删除了对它的引用。

答案 2 :(得分:2)

对象应该容忍Dispose被调用不止一次,因为 - 特别是在存在异常的情况下 - 清理代码可能很难确定哪些东西已被清理,哪些没有。在清除IDisposable字段时清除它们(并且容忍已经为空的字段)将使得更容易避免对Dispose的冗余调用,但是它不会花费任何东西来使对象容忍多次处理,并且它有助于在某些抛出异常的情况下避免ickiness。

答案 3 :(得分:-1)

如果处理了一个对象,则不应再次处理它。这有助于您不延长垃圾收集器中对象的使用寿命。

我通常使用的模式就是这个。

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