如果一个类型中包含了非托管的资源,那么我们应该自己编写释放非托管资源的方法。.NET提供了一个标准的用于释放资源的模式,叫做Dispose模式,在这种模式中,类型实现IDisposable接口,并提供一个终结器。这样,正常流程下类型的使用者调用Dispose()方法来释放资源,如果用户忘记调用Dispose()方法, 那么类型的终结器会作为最后的保障来释放对象的非托管资源。

在Dispose模式中,类层次中的根基类应该实现IDisposable接口来释放非托管资源,并且该类型应该添加一个终结器作为保障机制。同时这两个方法最后都要把自己释放资源的方法做成一个虚方法,这样派生类可以重写该虚方法来满足自己的资源管理需要,只有在派生类必须释放自己的资源时,它才需要重写基类中的虚方法,而且必须要调用基类的虚方法。

当垃圾回收器运行时,它会立即释放那些没有终结器的垃圾对象的内存。所有实现了终结器的对象都仍然存储在内存中,但是会添加到一个终结队列中,同时垃圾回收器会创建一个线程来运行这些对象上的终结器,在终结器线程完成它的工作后,这些垃圾对象的内存才有可能被彻底删除。这样需要终结操作的对象会比美元后终结器的对象在内存中停留的时间更长。

当我们实现IDisposable接口时,需要在Dispose()方法中完成以下任务:

  1. 释放所有的非托管资源。
  2. 释放所有的托管资源。
  3. 设置一个状态标记,表明这个对象已经执行过Dispose()方法。
  4. 取消对象的终结奥做需求,我们可以通过调用GC.SuppressFinalize(this)来实现。

在一个拥有完整类继承的层次结构中,我们可以按如下方式编写基类释放资源的代码。

代码

1 public class MyResourceHog : IDisposable
2 {
3 // Flag for already disposed
4   private bool _alreadyDisposed = false;
5
6 // finalizer:
7 // Call the virtual Dispose method.
8   ~MyResourceHog()
9 {
10 Dispose( false );
11 }
12
13 // Implementation of IDisposable.
14 // Call the virtual Dispose method.
15 // Suppress Finalization.
16   public void Dispose()
17 {
18 Dispose( true );
19 GC.SuppressFinalize( true );
20 }
21
22 // Virtual Dispose method
23   protected virtual void Dispose( bool isDisposing )
24 {
25 // Don't dispose more than once.
26   if ( _alreadyDisposed )
27 return;
28 if ( isDisposing )
29 {
30 // TODO: free managed resources here.
31   }
32 // TODO: free unmanaged resources here.
33 // Set disposed flag:
34   _alreadyDisposed = true;
35 }
36 }
37  

上述代码中,由于终结器和Dispose()方法都会有释放资源的需要,因此,将这部分代码单独提取成一个虚方法,这样派生类如果有自己释放资源的需求,可以重写这个方法。

子类的代码如下所示。

代码

1 public class DerivedResourceHog : MyResourceHog
2 {
3 // Have its own disposed flag.
4 private bool _disposed = false;
5
6 protected override void Dispose( bool isDisposing )
7 {
8 // Don't dispose more than once.
9 if ( _disposed )
10 return;
11 if ( isDisposing )
12 {
13 // TODO: free managed resources here.
14 }
15 // TODO: free unmanaged resources here.
16
17 // Let the base class free its resources.
18 // Base class is responsible for calling
19 // GC.SuppressFinalize( )
20 base.Dispose( isDisposing );
21
22 // Set derived class disposed flag:
23 _disposed = true;
24 }
25 }

注意,上述代码中,基类和派生类中都包含有一个标记,来设置对象的释放状态,这就是一种防御策略,在类层次中重复使用这样的标记,可以把“对象释放过程中所出现的任何可能的错误”封装在类层次中的一个类型中,而不是组成对象的多个类型内。我们之所以需要这样做,是因为我们可能会碰到在实例被释放后调用Dispose()方法,如果一个实例已经被释放过了,那么在调用它的Dispose()方法或者Finalize()方法,应该什么都不做。

当我们在编写Dispose()方法或者Finalize()方法时,需要注意一点:这些方法应该只负责释放资源的工作,不应该包含其他业务逻辑。对于一个对象来说,它的生命周期始于构造函数,终于垃圾回收。在对象调用了Dispose()方法但是内存还没有被删除的这段时间里,我们可以认为对象处于“昏迷”状态,如果我们在Dispose()方法,那么很可能会将对象重新置为可用,这样会扰乱对象的生命周期。

我们来看下面的代码。

代码

1 public class BadClass
2 {
3 // Store a reference to a global object:
4 private readonly ArrayList _finalizedList;
5 private string _msg;
6
7 public BadClass( ArrayList badList, string msg )
8 {
9 // cache the reference:
10 _finalizedList = badList;
11 _msg = (string)msg.Clone();
12 }
13
14 ~BadClass()
15 {
16 // Add this object to the list.
17 // This object is reachable, no
18 // longer garbage. It's Back!
19 _finalizedList.Add( this );
20 }
21 }

上述代码在析构函数中,又将对象本身放入到一个列表中,这样对象本身又变为可用的,这样做是不正确的。

Effective C# Item18:实现标准Dispose模式相关推荐

  1. 行动力决定了一个人的成败,有想法,就去做! C#的内存管理原理解析+标准Dispose模式的实现

    尽管.NET运行库负责处理大部分内存管理工作,但C#程序员仍然必须理解内存管理的工作原理,了解如何高效地处理非托管的资源,才能在非常注重性能的系统中高效地处理内存. C#编程的一个优点就是程序员不必担 ...

  2. dispose 模式 java_C#中标准Dispose模式的实现

    需要明确一下C#程序(或者说.NET)中的资源.简单的说来,C#中的每一个类型都代表一种资源,而资源又分为两类: 托管资源:由CLR管理分配和释放的资源,即由CLR里new出来的对象: 非托管资源:不 ...

  3. Effective C# 原则18:实现标准的处理(Dispose)模式(译)

    Effective C# 原则18:实现标准的处理(Dispose)模式 我们已经讨论过,处理一个占用了非托管资源对象是很重要的.现在是时候来讨论如何写代码来管理这些类占用的非内存资源了.一个标准的模 ...

  4. 利用C#实现标准的 Dispose模式

    我们已经知道了处置那些占用非受控(unmanaged)资源的对象的重要性,现在应该编写资源管理代码来处置那些包含非内存资源的类型了.整个.NET框架组件都使用一个标准的模式来处理非内存资源.使用你建立 ...

  5. C#实现标准的Dispose模式

    上一章说过,对于对象包含非托管资源,要正确的加以清理.对于非托管资源来说,.net framework 会采用一套标准的模式来完成清理工作,因此,如果你编写的类里面用到了非托管资源,那么该类的使用者就 ...

  6. C#中的Dispose模式

    C#中的资源 在我们的程序中,使用资源后,需要释放.那么在C#中的每一种资源,可以分为两类: - 托管资源:由CLR管理分配和释放的资源,即由CLR里new出来的对象: - 非托管资源:不受CLR管理 ...

  7. C# Dispose模式

    目的 为了及时释放宝贵的非托管资源和托管资源,并且保证资源在被 gc 回收的时候可以正确释放资源,同时兼顾执行效率. 必须遵循的事实 1 .  托管资源释放: 由另一线程的 gc 进行释放,当托管的对 ...

  8. 编写高质量代码改善C#程序的157个建议——建议50:在Dispose模式中应区别对待托管资源和非托管资源...

    建议50:在Dispose模式中应区别对待托管资源和非托管资源 真正资源释放代码的那个虚方法是带一个bool参数的,带这个参数,是因为我们在资源释放时要区别对待托管资源和非托管资源. 提供给调用者调用 ...

  9. C# Dispose模式详细分析

    C#Dispose模式 目的: 为了及时释放宝贵的非托管资源和托管资源,并且保证资源在被gc回收的时候可以正确释放资源,同时兼顾执行效率 必须遵循的事实: 1 托管资源释放: 由另一线程的gc进行释放 ...

最新文章

  1. 大一期末考试,python,测试题,含答案
  2. JS基础 -- 枚举对象中的属性
  3. 基于增强现实和脑机接口的机械臂控制系统
  4. [导入]C#优化字符串操作【月儿原创】
  5. java写dnf外掛_dnf卡盟_Java的泛型详解(一)
  6. CSS行内元素和块级元素的水平居中,垂直居中,水平垂直居中实现
  7. 【PAT - 甲级1012】The Best Rank (25分)
  8. jvm调优 java_opt_Java-100天知识进阶-JVM调优工具-JDK自带工具-知识铺《八》
  9. 小程序 - 腾讯云 - wafer - PHP - 数据库接口的应用和研究 - 01 - DB::insert
  10. flutter 禁止冒泡_【Flutter】Switch开关组件
  11. sprintf使用时需要注意的问题
  12. Python实战入门到精通第一讲——函数
  13. 以MQL5 编写的EA 交易程序的测试与优化指南
  14. 5wpa_supplicant程序 --详解
  15. velocity模板使用手册
  16. 高性能计算专业应用软件大观
  17. 关于win11右键的慢问题的建议
  18. 怎样把pdf格式转换成jpg
  19. css3学习以及移动端开发基本概念的思考
  20. 检测浏览器是pc端还是移动端 是否微信浏览器

热门文章

  1. 2018 开源分布式中间件 DBLE 年报
  2. CGMP, IGMP Snooping and RGMP
  3. 物联网哪个市场有可能诞生新一轮BAT?
  4. 常规RPC通讯过程【转载】
  5. UVALive 3211 Now or Later (2-SAT)
  6. linux命令学习——tar
  7. jquery插件开发方法
  8. 程序员专属段子集锦 4/10
  9. Linux 命令(111)—— alias 命令(builtin)
  10. MySQL 导入 csv、excel 或者 sql 文件