文档章节

在C#中使用Finalize / Dispose方法

 技术盛宴
发布于 02/17 20:32
字数 2306
阅读 70
收藏 0

3 月,跳不动了?>>>

C#2008

我已经为此工作了一段时间,但我仍然对在代码中使用finalize和dispose方法感到困惑。 我的问题如下:

  1. 我知道在处理非托管资源时我们只需要一个终结器即可。 但是,如果存在调用非托管资源的托管资源,是否仍需要实现终结器?

  2. 但是,如果我开发的类不直接或间接使用任何非托管资源,我是否应该实现IDisposable以允许该类的客户端使用“ using语句”?

    实现IDisposable只是为了使您的类的客户端能够使用using语句是否可行?

    using(myClass objClass = new myClass()) { // Do stuff here }
  3. 我在下面开发了这个简单的代码来演示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); } }

有关源代码的问题:

  1. 在这里,我没有添加终结器,通常,终结器将由GC调用,而终结器将调用Dispose。 由于没有终结器,我什么时候调用Dispose方法? 是该类的客户必须调用它吗?

    因此,在示例中,我的类称为NoGateway,客户端可以使用和处置此类,如下所示:

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

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

    NoGateway objNoGateway = new NoGateway(); // Do stuff with object objNoGateway.Dispose(); // finished with it
  2. 我在NoGateway类中使用WebClient类。 因为WebClient实现了IDisposable接口,这是否意味着WebClient间接使用非托管资源? 有严格的规则可以遵循吗? 我怎么知道一个类使用非托管资源?


#1楼

请注意,任何IDisposable实现都应遵循以下模式(IMHO)。 我根据几个优秀的.NET“神”, .NET Framework设计指南中的信息开发了这种模式(请注意,由于某些原因,MSDN不遵循此原则!)。 .NET Framework设计指南由Krzysztof Cwalina(当时为CLR架构师)和Brad Abrams(我相信当时为CLR程序经理)和Bill Wagner([有效的C#]和[更有效的C#](在Amazon.com上查找以下内容:

请注意,除非您的类直接包含(而不继承)非托管资源,否则切勿实现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 );
    }
}

我已在以下博客上发布了此实现: 如何正确实现处置模式


#2楼

没有人回答即使您不需要IDisposable也应该实现IDisposable的问题。

简短答案:否

长答案:

这将允许您的类的使用者使用“使用”。 我要问的问题是-他们为什么要这样做? 除非大多数开发人员知道必须使用以及如何知道,否则他们将不使用“使用”。 要么

  • 它避免了他们的经验(例如套接字类)
  • 其记录
  • 他们非常谨慎,可以看到该类实现了IDisposable

因此,通过实现IDisposable,您可以告诉开发人员(至少一些)此类包装了必须释放的内容。 他们将使用“使用”-但在其他情况下,则不可能使用(对象的范围不是局部的)。 在其他情况下,他们将不得不开始担心对象的寿命-我肯定会担心。 但这不是必需的

您实现了Idisposable以使他们能够使用using,但是除非您告知他们使用,否则他们将不会使用using。

所以不要做


#3楼

我同意pm100 (并且应该在我之前的帖子中明确地说过这一点)。

除非需要,否则永远不要在类中实现IDisposable。 具体来说,大约有5次需要/应该实现IDisposable的时间:

  1. 您的类明确包含(即不通过继承)任何实现IDisposable的托管资源,一旦不再使用该类,应将其清除。 例如,如果您的类包含Stream,DbCommand,DataTable等的实例。

  2. 您的类明确包含任何实现Close()方法的托管资源,例如IDataReader,IDbConnection等。请注意,其中某些类确实通过具有Dispose()和Close()方法来实现IDisposable。

  3. 您的类显式包含非托管资源-例如,COM对象,指针(是的,您可以在托管C#中使用指针,但是必须在“不安全”块中声明它们,等等。对于非托管资源,还应确保在RCW上调用System.Runtime.InteropServices.Marshal.ReleaseComObject()尽管从理论上讲,RCW是托管包装,但在幕后仍然存在引用计数。

  4. 如果您的班级使用强引用订阅事件。 您需要取消注册/从事件中分离出来。 在尝试注销/分离它们之前,请务必先确保它们不为null!

  5. 您的课程包含以上内容的任意组合...

对于使用COM对象并必须使用Marshal.ReleaseComObject()的建议替代方法是使用System.Runtime.InteropServices.SafeHandle类。

BCL(基类库团队)在此处有一篇不错的博客文章, 网址为http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx

需要特别注意的一点是,如果您正在使用WCF并清理资源,则应始终避免使用“ using”块。 在MSDN上有很多博客文章,还有一些关于为什么这是一个坏主意的文章。 我也在这里发布了相关内容- 不要在WCF代理中使用“ using()”


#4楼

实施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
  }

}

#5楼

来自msdn的模式

public class BaseResource: IDisposable
{
   private IntPtr handle;
   private Component Components;
   private bool disposed = false;
   public BaseResource()
   {
   }
   public void Dispose()
   {
      Dispose(true);      
      GC.SuppressFinalize(this);
   }
   protected virtual void Dispose(bool disposing)
   {
      if(!this.disposed)
      {        
         if(disposing)
         {
            Components.Dispose();
         }         
         CloseHandle(handle);
         handle = IntPtr.Zero;
       }
      disposed = true;         
   }
   ~BaseResource()      
   {      Dispose(false);
   }
   public void DoSomething()
   {
      if(this.disposed)
      {
         throw new ObjectDisposedException();
      }
   }
}
public class MyResourceWrapper: BaseResource
{
   private ManagedResource addedManaged;
   private NativeResource addedNative;
   private bool disposed = false;
   public MyResourceWrapper()
   {
   }
   protected override void Dispose(bool disposing)
   {
      if(!this.disposed)
      {
         try
         {
            if(disposing)
            {             
               addedManaged.Dispose();         
            }
            CloseHandle(addedNative);
            this.disposed = true;
         }
         finally
         {
            base.Dispose(disposing);
         }
      }
   }
}

本文转载自:https://stackoom.com/question/3lpE/在C-中使用Finalize-Dispose方法

粉丝 0
博文 1550
码字总数 0
作品 0
深圳
高级程序员
私信 提问
加载中

评论(0)

补充ZendyHu对Finalize和IDispose异同的说明

ZendyHu 在他的文章 对.Net 垃圾回收的C#编程相关方面(Finalize 和Dispose(bool disposing)和 Dispose())的一些理解体会 中说了一下他对于.net垃圾回收的一些理解,不过个人认为他并没有说的...

长平狐
2012/10/23
26
0
C#垃圾回收Finalize 和Dispose的理解

C# 中的析构函数实际上是重写了 System.Object 中的虚方法 Finalize 三种最常的方法如下:   1. 析构函数;(由GC调用,不确定什么时候会调用)   2. 继承IDisposable接口,实现Dispo...

格式化记忆
2016/02/25
285
0
.Net确定销毁

.Net中,垃圾回收器负责回收你创建的引用类型的对象,但是回收时间并不能准确估计出来,所以这称之为非确定销毁。值类型自动释放,所以不在本文讨论之中。 但是有些稀缺资源,比如文件句柄、...

长平狐
2012/08/28
77
0
.NET面试题解析(06)-GC与内存管理

转自:http://www.cnblogs.com/anding/p/5260319.html 常见面试题目: 1. 简述一下一个引用对象的生命周期? 2. 创建下面对象实例,需要申请多少内存空间? public class User{ } 3. 什么是垃圾...

莫问今朝乄
2018/02/01
0
0
从C#垃圾回收(GC)机制中挖掘性能优化方案

  GC,Garbage Collect,中文意思就是垃圾回收,指的是系统中的内存的分配和回收管理。其对系统性能的影响是不可小觑的。今天就来说一下关于GC优化的东西,这里并不着重说概念和理论,主要...

雲霏霏
2014/09/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Flutter 强大的MediaQuery控件

注意:无特殊说明,Flutter版本及Dart版本如下: Flutter版本: 1.12.13+hotfix.5 Dart版本: 2.7.0 MediaQuery 通常情况下,不会直接将MediaQuery当作一个控件,而是使用MediaQuery.of获取当...

老孟程序员
14分钟前
12
0
【实战】2.如何写周报

如何写周报 一、周报的目的 以一个时间节点为准,同时做到向上汇报和向下汇报。向上汇报要做到整体项目的概况,让上级领导知道当前项目的整体状态。向下汇报要做到我们当前做了什么,紧接着的...

卖小女孩的小火柴
21分钟前
18
0
美颜重磅技术之GPUImage源码分析

说到基于GPU的图像处理和实时滤镜,大家肯定会想到鼎鼎大名的GPUImage,这个项目确实为后续开发提供了很多方便,基本的图像处理工具一应俱全。但是学习借鉴GPUImage的项目结构,可以为我们提...

码农突围
27分钟前
7
0
mapbox

Mapbox是一个可以跨行业使用的开发平台,我们可以利用它对地图进行创建和定制,以解决地图、数据和空间分析等问题。 Leaflet 轻量 WebGIS 前端类库 Leaflet 是一个为建设移动设备友好的互动地...

东东笔记
33分钟前
32
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部