在C#中完成/处理模式

C#2008

我一直在研究这个问题一段时间,而且我仍然对一些问题感到困惑。 我的问题如下

  • 我知道如果你正在处理非托管资源,你只需要一个终结器。 但是,如果您使用调用非托管资源的托管资源,您是否仍需要实现终结器?

  • 但是,如果您直接或间接开发一个不使用任何非托管资源的类,是否可以实现IDisposable以便您的类的客户端可以使用“使用语句”?

    实施IDisposable只是为了让您的班级的客户可以使用using语句是否可以接受?

    using(myClass objClass = new myClass())
    {
        // Do stuff here
    }
    
  • 我在下面开发了这个简单的代码来演示Finalize / dispose模式:

    public class NoGateway : IDisposable
    {
        private WebClient wc = null;
    
        public NoGateway()
        {
            wc = new WebClient();
            wc.DownloadStringCompleted += wc_DownloadStringCompleted;
        }
    
    
        // Start the Async call to find if NoGateway is true or false
        public void NoGatewayStatus()
        {
            // Start the Async's download
                // Do other work here
            wc.DownloadStringAsync(new Uri(www.xxxx.xxx));
        }
    
        private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            // Do work here
        }
    
        // Dispose of the NoGateway object
        public void Dispose()
        {
            wc.DownloadStringCompleted -= wc_DownloadStringCompleted;
            wc.Dispose();
            GC.SuppressFinalize(this);
        }
    }
    
  • 关于源代码的问题:

  • 这里我没有添加终结器,通常终止器将由GC调用,终结器将调用Dispose。 由于我没有终结器,我什么时候可以调用Dispose方法? 它是这个班的客户必须打电话吗?

    所以我的例子中的类被称为NoGateway,客户端可以像这样使用和处理类:

    using(NoGateway objNoGateway = new NoGateway())
    {
        // Do stuff here   
    }
    

    当执行到达使用块的末尾时,会自动调用Dispose方法,还是客户端不得不手动调用dispose方法? 即

    NoGateway objNoGateway = new NoGateway();
    // Do stuff with object
    objNoGateway.Dispose(); // finished with it
    
  • 我在NoGateway类中使用NoGateway类。 由于webclient实现了IDisposable接口,这是否意味着webclient间接使用非托管资源? 对此有没有严格而快速的规定? 我如何知道一个班级使用非托管资源?


  • 建议的IDisposable模式在这里。 在编写使用IDisposable的类时,通常应使用两种模式:

    在实现不使用非托管资源的密封类时,只需像使用常规接口一样实施Dispose方法:

    public sealed class A : IDisposable
    {
        public void Dispose()
        {
            // get rid of managed resources, call Dispose on member variables...
        }
    }
    

    当实现一个非密封类时,请这样做:

    public class B : IDisposable
    {    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // get rid of managed resources
            }   
            // get rid of unmanaged resources
        }
    
        // only if you use unmanaged resources directly in B
        //~B()
        //{
        //    Dispose(false);
        //}
    }
    

    注意我没有在B声明一个终结器; 你应该只实现一个终结器,如果你有实际的非托管资源来处置。 即使已调用SuppressFinalize ,CLR也会以不同于不可终止对象的方式处理可终结对象。

    所以,除非必须声明一个终结器,否则你不应该声明一个终结器,但是如果他们直接使用非托管资源,你可以给你的类的继承者一个钩子来调用你的Dispose并实现一个终结器:

    public class C : B
    {
        private IntPtr m_Handle;
    
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                // get rid of managed resources
            }
            ReleaseHandle(m_Handle);
    
            base.Dispose(disposing);
        }
    
        ~C() {
            Dispose(false);
        }
    }
    

    如果您没有直接使用非托管资源( SafeHandle和朋友不计算,因为他们声明了自己的终结器),那么不要实现终结器,因为GC会以不同的方式处理可终结类,即使后来禁止终结器。 还要注意的是,即使B没有终结器,它仍然会调用SuppressFinalize来正确处理任何实现终结器的子类。

    当一个类实现了IDisposable接口时,这意味着某个地方有一些非托管资源应该在完成使用该类时摆脱掉。 实际资源被封装在类中; 你不需要明确地删除它们。 只需调用Dispose()using(...) {}封装类将确保任何非托管资源在必要时被删除。


    实施IDisposable的官方模式很难理解。 我相信这个更好:

    public class BetterDisposableClass : IDisposable {
    
      public void Dispose() {
        CleanUpManagedResources();
        CleanUpNativeResources();
        GC.SuppressFinalize(this);
      }
    
      protected virtual void CleanUpManagedResources() { 
        // ...
      }
      protected virtual void CleanUpNativeResources() {
        // ...
      }
    
      ~BetterDisposableClass() {
        CleanUpNativeResources();
      }
    
    }
    

    一个更好的解决方案是有一个规则,你总是必须为你需要处理的任何非托管资源创建一个包装类:

    public class NativeDisposable : IDisposable {
    
      public void Dispose() {
        CleanUpNativeResource();
        GC.SuppressFinalize(this);
      }
    
      protected virtual void CleanUpNativeResource() {
        // ...
      }
    
      ~NativeDisposable() {
        CleanUpNativeResource();
      }
    
    }
    

    使用SafeHandle及其衍生工具,这些类应该非常少见。

    即使在存在继承的情况下,不直接处理非托管资源的可抛弃类的结果也非常强大: 它们不再需要关注非托管资源 。 他们将很容易实现并理解:

    public class ManagedDisposable : IDisposable {
    
      public virtual void Dispose() {
        // dispose of managed resources
      }
    
    }
    

    请注意,任何IDisposable实施应遵循以下模式(恕我直言)。 我基于来自几个优秀的.NET“众神”.NET框架设计指南的信息开发了这种模式(请注意,由于某些原因,MSDN不遵循这一点!)。 .NET框架设计指南由Krzysztof Cwalina(当时的CLR架构师)和Brad Abrams(我相信当时的CLR程序经理)和Bill Wagner([Effective C#]和[更高效的C#](只需要一个在Amazon.com上查找这些内容:

    请注意,除非您的课程直接包含(不继承)非托管资源,否则您绝不应该实施终结器。 一旦你在一个类中实现了一个Finalizer,即使它从未被调用过,它也会保证为额外的集合而生活。 它会自动放置在Finalization Queue(在单个线程上运行)上。 另外,一个非常重要的注意事项...在Finalizer中执行的所有代码(如果您需要实现其中一个),必须是线程安全的并且是异常安全的! 不好的事情会发生,否则......(即未确定的行为,并在例外情况下,致命的不可恢复的应用程序崩溃)。

    我放在一起的模式(并写了一段代码片段)如下:

    #region IDisposable implementation
    
    //TODO remember to make this class inherit from IDisposable -> $className$ : IDisposable
    
    // Default initialization for a bool is 'false'
    private bool IsDisposed { get; set; }
    
    /// <summary>
    /// Implementation of Dispose according to .NET Framework Design Guidelines.
    /// </summary>
    /// <remarks>Do not make this method virtual.
    /// A derived class should not be able to override this method.
    /// </remarks>
    public void Dispose()
    {
        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.
    
        // Always use SuppressFinalize() in case a subclass
        // of this type implements a finalizer.
        GC.SuppressFinalize( this );
    }
    
    /// <summary>
    /// Overloaded Implementation of Dispose.
    /// </summary>
    /// <param name="isDisposing"></param>
    /// <remarks>
    /// <para><list type="bulleted">Dispose(bool isDisposing) executes in two distinct scenarios.
    /// <item>If <paramref name="isDisposing"/> equals true, the method has been called directly
    /// or indirectly by a user's code. Managed and unmanaged resources
    /// can be disposed.</item>
    /// <item>If <paramref name="isDisposing"/> equals false, the method has been called by the 
    /// runtime from inside the finalizer and you should not reference 
    /// other objects. Only unmanaged resources can be disposed.</item></list></para>
    /// </remarks>
    protected virtual void Dispose( bool isDisposing )
    {
        // TODO If you need thread safety, use a lock around these 
        // operations, as well as in your methods that use the resource.
        try
        {
            if( !this.IsDisposed )
            {
                if( isDisposing )
                {
                    // TODO Release all managed resources here
    
                    $end$
                }
    
                // TODO Release all unmanaged resources here
    
    
    
                // TODO explicitly set root references to null to expressly tell the GarbageCollector
                // that the resources have been disposed of and its ok to release the memory allocated for them.
    
    
            }
        }
        finally
        {
            // explicitly call the base class Dispose implementation
            base.Dispose( isDisposing );
    
            this.IsDisposed = true;
        }
    }
    
    //TODO Uncomment this code if this class will contain members which are UNmanaged
    // 
    ///// <summary>Finalizer for $className$</summary>
    ///// <remarks>This finalizer will run only if the Dispose method does not get called.
    ///// It gives your base class the opportunity to finalize.
    ///// DO NOT provide finalizers in types derived from this class.
    ///// All code executed within a Finalizer MUST be thread-safe!</remarks>
    //  ~$className$()
    //  {
    //     Dispose( false );
    //  }
    #endregion IDisposable implementation
    

    以下是在派生类中实现IDisposable的代码。 请注意,您不需要在派生类的定义中显式列出来自IDisposable的继承。

    public DerivedClass : BaseClass, IDisposable (remove the IDisposable because it is inherited from BaseClass)
    
    
    protected override void Dispose( bool isDisposing )
    {
        try
        {
            if ( !this.IsDisposed )
            {
                if ( isDisposing )
                {
                    // Release all managed resources here
    
                }
            }
        }
        finally
        {
            // explicitly call the base class Dispose implementation
            base.Dispose( isDisposing );
        }
    }
    

    我已经在我的博客上发布了这个实现:如何正确地实现配置模式

    链接地址: http://www.djcxy.com/p/9209.html

    上一篇: Finalize/Dispose pattern in C#

    下一篇: Difference between @classmethod and a method in python