代码之家  ›  专栏  ›  技术社区  ›  Chriso

为什么IDisposable实现的设计方式是[关闭的]

  •  11
  • Chriso  · 技术社区  · 16 年前

    [ComVisible(true)]
    public interface IDisposable
    {
        void Dispose();
    }
    

    以及MSDN推荐的典型实现(我省略了检查当前对象是否已被处置):

    public class Base : IDisposable
    {
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // release managed
            }
            // release unmanaged
            disposed = true;
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        ~Base()
        {
            Dispose(false);
        }
    }
    
    public class Derived : Base
    {
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            if (disposing)
            {
                // release managed
            }
            // release unmanaged
            disposed = true;
        }
    }
    

    假定 该基类正确地实现了IDisposable,然后重写了Dispose(bool),它甚至不是原始接口的一部分。

    public class Base : IDisposable
    {
        public virtual void Dispose()
        {
            // release managed and unmanaged
            GC.SuppressFinalize(this);
        }
    
        ~Base()
        {
            // release unmanaged
        }
    }
    
    public class Derived : Base
    {
        public override void Dispose()
        {
            // release managed and unmanaged
            base.Dispose();
        }
    
        ~Derived()
        {
            // release unmanaged
        }
    }
    

    对我来说,这个实现更加清晰和一致。当然,不好的是我们必须在两个不同的地方释放非托管资源,但重要的是,可能超过99%的自定义类没有任何非托管资源可供处置,因此它们无论如何都不需要终结器。我无法向初级程序员解释为什么MSDN实现更好,因为我自己并不真正理解它。

    所以我想知道,是什么导致了这种不寻常的设计决策(使派生类重写不同于接口中的方法,并使他考虑它最可能不包含的非托管资源)。对这件事有什么想法吗?

    8 回复  |  直到 14 年前
        1
  •  8
  •   Reed Copsey    16 年前

    所以我想知道,是什么导致了这种不寻常的设计决策(使派生类重写不同于接口中的方法,并使他考虑它最可能不包含的非托管资源)。对这件事有什么想法吗?

    主要问题是IDisposable是在框架已经设计并存在之后定义的。这意味着要处理托管代码试图避免的情况——因此,如果这是一种非常常见的情况,那么它实际上是一种边缘情况

    顺便说一句,如果您查看C++/CLI,就可以看到这一点。它是在IDisposable之后设计的,因此以更自然的方式实现IDisposable(析构函数)[ ~ClassName ]自动成为Dispose和Finalizer[ !ClassName ]被视为终结器)。

    另一个问题是IDisposable可以处理多种情况。我写了一封信 entire blog series ,遍历包装本机代码时应使用的不同实现,封装实现IDisposable的类,并将其与分解类型一起使用。

    必须 protected virtual void Dispose(bool disposing) 方法允许额外的灵活性,这在公共接口中是不容易、安全地处理的。

        2
  •  12
  •   user1228 user1228    16 年前

    我通常对派生类进行猜测。这是我的.snippet文件:

    #region IDisposable pattern
    /// <summary>
    /// Dispose of (clean up and deallocate) resources used by this class.
    /// </summary>
    /// <param name="fromUser">
    /// True if called directly or indirectly from user code.
    /// False if called from the finalizer (i.e. from the class' destructor).
    /// </param>
    /// <remarks>
    /// When called from user code, it is safe to clean up both managed and unmanaged objects.
    /// When called from the finalizer, it is only safe to dispose of unmanaged objects.
    /// This method should expect to be called multiple times without causing an exception.
    /// </remarks>
    protected virtual void Dispose(bool fromUser)
        {
        if (fromUser)   // Called from user code rather than the garbage collector
            {
            // Dispose of managed resources (only safe if called directly or indirectly from user code).
            try
                {
          DisposeManagedResources();
                GC.SuppressFinalize(this);  // No need for the Finalizer to do all this again.
                }
            catch (Exception ex)
                {
                //ToDo: Handle any exceptions, for example produce diagnostic trace output.
                //Diagnostics.TraceError("Error when disposing.");
                //Diagnostics.TraceError(ex);
                }
            finally
                {
                //ToDo: Call the base class' Dispose() method if one exists.
                //base.Dispose();
                }
            }
        DisposeUnmanagedResources();
        }
    /// <summary>
    /// Called when it is time to dispose of all managed resources
    /// </summary>
      protected virtual void DisposeManagedResources(){}
    /// <summary>
    /// Called when it is time to dispose of all unmanaged resources
    /// </summary>
      protected virtual void DisposeUnmanagedResources(){}
    /// <summary>
    /// Dispose of all resources (both managed and unmanaged) used by this class.
    /// </summary>
    public void Dispose()
        {
        // Call our private Dispose method, indicating that the call originated from user code.
        // Diagnostics.TraceInfo("Disposed by user code.");
        this.Dispose(true);
        }
    /// <summary>
    /// Destructor, called by the finalizer during garbage collection.
    /// There is no guarantee that this method will be called. For example, if <see cref="Dispose"/> has already
    /// been called in user code for this object, then finalization may have been suppressed.
    /// </summary>
    ~$MyName$()
        {
        // Call our private Dispose method, indicating that the call originated from the finalizer.
        // Diagnostics.TraceInfo("Finalizer is disposing $MyName$ instance");
        this.Dispose(false);
        }
    #endregion
    
        3
  •  3
  •   Jonathan Allen    16 年前

    框架设计指南:可重用.NET库的约定、习惯用法和模式 http://www.amazon.com/gp/product/0321545613?ie=UTF8&tag=bradabramsblo-20&link_code=wql&camp=212361&creative=380601

    这是微软员工用来构建.NET API的一套规则。规则是免费的(见下文),但书中有解释规则的注释。它确实是.NET开发人员的必备工具。

    http://msdn.microsoft.com/en-us/library/b1yfkh5e.aspx

        4
  •  2
  •   Jordão    16 年前

    better :

    public class DisposableClass : IDisposable {
      void IDisposable.Dispose() {
        CleanUpManagedResources();
        CleanUpNativeResources();
        GC.SuppressFinalize(this);
      }
    
      protected virtual void CleanUpManagedResources() { 
        // ...
      }
      protected virtual void CleanUpNativeResources() {
        // ...
      }
    
      ~DisposableClass() {
        CleanUpNativeResources();
      }
    }
    
        5
  •  1
  •   SLaks    16 年前

    article about this pattern .

    这并不能完全回答这个问题,但是您可以使用下面的代码片段来实现该模式。

    <?xml version="1.0" encoding="utf-8" ?>
    <CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
        <CodeSnippet Format="1.0.0">
            <Header>
                <Title>Dispose pattern</Title>
                <Shortcut>dispose</Shortcut>
                <Description>Code snippet for virtual dispose pattern</Description>
                <Author>SLaks</Author>
                <SnippetTypes>
                    <SnippetType>Expansion</SnippetType>
                    <SnippetType>SurroundsWith</SnippetType>
                </SnippetTypes>
            </Header>
            <Snippet>
                <Declarations>
                    <Literal Editable="false">
                        <ID>classname</ID>
                        <ToolTip>Class name</ToolTip>
                        <Default>ClassNamePlaceholder</Default>
                        <Function>ClassName()</Function>
                    </Literal>
                </Declarations>
                <Code Language="csharp">
                    <![CDATA[
            ///<summary>Releases unmanaged resources and performs other cleanup operations before the $classname$ is reclaimed by garbage collection.</summary>
            ~$classname$() { Dispose(false); }
            ///<summary>Releases all resources used by the $classname$.</summary>
            public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
            ///<summary>Releases the unmanaged resources used by the $classname$ and optionally releases the managed resources.</summary>
            ///<param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
            protected virtual void Dispose(bool disposing) {
                if (disposing) {
                    $end$$selected$
                }
            }]]>
                </Code>
            </Snippet>
        </CodeSnippet>
    </CodeSnippets>
    
        6
  •  0
  •   BarrettJ    16 年前

    我的理解是,IDisposable的全部原因是为了释放非托管资源,所以我不明白为什么您要说“99%的自定义类没有任何非托管的可处置内容”——如果您要实现IDisposable,应该是因为您有非托管资源。

    MSDN IDisposable

        7
  •  0
  •   SwDevMan81    16 年前

        8
  •  0
  •   supercat    14 年前

    推荐的IDisposable模式的一个有用特性是,它允许派生类型使用一致的模式来扩展实现IDisposable的类型, . 如果将参数视为一个伪参数,它只是用来给受保护的方法一个不同于无参数Dispose()的签名,那么这种模式真的不太糟糕;最大的缺点是防止冗余处置的保护没有以线程安全的方式执行。