【C#学习笔记】 IDisposable 接口

托管资源指的是.NET可以自动进行回收的资源,主要是指托管堆上分配的内存资源。托管资源的回收工作是不需要人工干预的,有.NET运行库在合适调用垃圾回收器进行回收。

非托管资源指的是.NET不知道如何回收的资源,最常见的一类非托管资源是包装操作系统资源的对象,例如文件,窗口,网络连接,数据库连接,画刷,图标等。这类资源,垃圾回收器在清理的时候会调用Object.Finalize()方法。默认情况下,方法是空的,对于非托管对象,需要在此方法中编写回收非托管资源的代码,以便垃圾回收器正确回收资源。

在.NET中,Object.Finalize()方法是无法重载的,编译器是根据类的析构函数来自动生成Object.Finalize()方法的,所以对于包含非托管资源的类,可以将释放非托管资源的代码放在析构函数。

 注意,不能在析构函数中释放托管资源,因为析构函数是有垃圾回收器调用的,可能在析构函数调用之前,类包含的托管资源已经被回收了,从而导致无法预知的结果。

本来如果按照上面做法,非托管资源也能够由垃圾回收器进行回收,但是非托管资源一般是有限的,比较宝贵的,而垃圾回收器是由CRL自动调用的,这样就无法保证及时的释放掉非托管资源,因此定义了一个Dispose()方法,让使用者能够手动的释放非托管资源。Dispose()方法释放类的托管资源和非托管资源,使用者手动调用此方法后,垃圾回收器不会对此类实例再次进行回收。Dispose()方法是由使用者调用的,在调用时,类的托管资源和非托管资源肯定都未被回收,所以可以同时回收两种资源。

Microsoft为非托管资源的回收专门定义了一个接口:IDisposable,接口中只包含一个Dispose()方法。任何包含非托管资源的类,都应该继承此接口。

在一个包含非托管资源的类中,关于资源释放的标准做法是:

(1)     继承IDisposable接口;

(2)     实现Dispose()方法,在其中释放托管资源和非托管资源,并将对象本身从垃圾回收器中移除(垃圾回收器不在回收此资源);

(3)     实现类析构函数,在其中释放非托管资源。

在使用时,显示调用Dispose()方法,可以及时的释放资源,同时通过移除Finalize()方法的执行,提高了性能;如果没有显示调用Dispose()方法,垃圾回收器也可以通过析构函数来释放非托管资源,垃圾回收器本身就具有回收托管资源的功能,从而保证资源的正常释放,只不过由垃圾回收器回收会导致非托管资源的未及时释放的浪费。

在.NET中应该尽可能的少用析构函数释放资源。在没有析构函数的对象在垃圾处理器一次处理中从内存删除,但有析构函数的对象,需要两次,第一次调用析构函数,第二次删除对象。而且在析构函数中包含大量的释放资源代码,会降低垃圾回收器的工作效率,影响性能。所以对于包含非托管资源的对象,最好及时的调用Dispose()方法来回收资源,而不是依赖垃圾回收器。

上面就是.NET中对包含非托管资源的类的资源释放机制,只要按照上面要求的步骤编写代码,类就属于资源安全的类。

下面用一个例子来总结一下.NET非托管资源回收机制:

Public class BaseResource:IDisposable

{

PrivateIntPtr handle; // 句柄,属于非托管资源

PrivateComponet comp; // 组件,托管资源

Privateboo isDisposed = false; // 是否已释放资源的标志

PublicBaseResource

{

}

//实现接口方法

//由类的使用者,在外部显示调用,释放类资源

Publicvoid Dispose()

{

Dispose(true);// 释放托管和非托管资源

//将对象从垃圾回收器链表中移除,

// 从而在垃圾回收器工作时,只释放托管资源,而不执行此对象的析构函数

GC.SuppressFinalize(this);

}

//由垃圾回收器调用,释放非托管资源

~BaseResource()

{

Dispose(false);// 释放非托管资源

}

//参数为true表示释放所有资源,只能由使用者调用

//参数为false表示释放非托管资源,只能由垃圾回收器自动调用

//如果子类有自己的非托管资源,可以重载这个函数,添加自己的非托管资源的释放

//但是要记住,重载此函数必须保证调用基类的版本,以保证基类的资源正常释放

Protectedvirtual void Dispose(bool disposing)

{

If(!this.disposed)// 如果资源未释放 这个判断主要用了防止对象被多次释放

{

If(disposing)

{

Comp.Dispose();// 释放托管资源

}

closeHandle(handle);// 释放非托管资源

handle= IntPtr.Zero;

}

this.disposed= true; // 标识此对象已释放

}

}

析构函数只能由垃圾回收器调用。

Despose()方法只能由类的使用者调用。

在C#中,凡是继承了IDisposable接口的类,都可以使用using语句,从而在超出作用域后,让系统自动调用Dispose()方法。
         一个资源安全的类,都实现了IDisposable接口和析构函数。提供手动释放资源和系统自动释放资源的双保险。

时间: 2024-12-23 11:55:40

【C#学习笔记】 IDisposable 接口的相关文章

Java学习笔记之接口

一.接口的概念与定义 首先考虑一个简单的接口的定义: public interface Output { int MAX_LINE = 40; void out(); void getData(String msg); } 定义接口使用关键字interface 修饰符interface前面的public可以省略,如果省略,则采用默认访问控制,即只有在相同包结构的代码才可以访问此接口 接口不可以有构造方法(区别于类中的构造方法) 接口里面的所有成员,包括常量.方法等都是public访问权限,所以在

Java学习笔记_23_List接口实现类

23.List接口实现类: List接口继承了Collection接口,它是一个允许存在重复项的有序集合. 1>实现类ArrayList: ArrayList类支持可随需要而增长的动态数组.数组列表以一个原大小被创建,当超过了它的大小, 类集自动增大,当对象被删除后,数组就可以缩小. 优点:ArrayList类对于使用索引取出元素用较高的效率,他可以用索引快速定位对象. 缺点:ArrayList类对于元素的删除或插入速度较慢. 构造方法: · ArrayList(): 构造一个初始容量为10的空

Java学习笔记_21_Collection接口

21.Collection接口: 1>容器类的添加.删除: · add(Object o) :将对象添加到集合. · Remove(Object o) :删除集合中与o相匹配的对象. 2>容器中类的查询: · Size(): 返回集合中元素的个数. · isEmpty(): 判断集合中是否包含元素. · contains(Object o): 判断集合中是否包含指定元素. · iterator(): 返回集合的一个迭代器,用来访问该集合的元素. · contains(Collection c)

Java学习笔记_24_Map接口

24.Map接口: Map接口定义了存储"键(key)-值(value)映射对"的方法,Map中不能有重复的"键", Map实现类中储存的"键-值"映射对是通过键来唯一标识的,Map底层的"键"使用Set来存放的, 所以Map中的映射对的"键"对应的类必须重写hashCode()和equals()方法, 常用String作为Map的"键".  Map的添加.删除操作: · Object 

No2_1.接口继承多态_Java学习笔记_接口

接口.继承与多态 1.继承和多态是面向对象开发语言中的重要一个环节,使用得当,可以将整个程序的架构变得非常有弹性,减少代码冗余: 2.继承:复用定义好的类: 3.多态:可以动态调整对象的调用,降低对象的依存关系: 4.接口:Java中类可以实现多个接口,被用来建立类与类之间的标准: ***接口***1.接口的目的:Java语言只支持单继承,即一个类只能有一个父类.实际应用中,经常需要多继承解决问题,为解决该问题,提供接口实现类的多重继承:2.接口定义:与类的定义类似,接口的声明和接口体:[修饰符

thinkinginjava学习笔记08_接口

抽象类和抽象方法 抽象方法是指没有具体实现的方法,仅仅有方法的声明和没有方法体:使用abstract关键字定义一个抽象方法:包含抽象方法的类成为抽象类,如果一个类中包含抽象方法则必须使用abstract来限定该类为抽象类:抽象类不能实例化对象,抽象类的子类必须对所有的抽象方法提供方法定义,否则仍然是抽象类,且必须用abstract来限定: 接口 接口是一个完全抽象的类,没有提供任何具体的实现,只提供了具体的方法形式(方法名.参数列表.返回值):由于类是通过接口和外部通信,接口被用来建立类和类之间

Java学习笔记之接口和抽象类

接口(interface)1.interface创建一个接口,implements实现接口 interface jiekou{} class lie implements jiekou{}2.接口可以有属性,但必须赋值,不建议在接口定义属性(可用于定义常量)3.接口,可以定义与接口同名的方法4.接口的所有方法都没有方法体 interface jiekou{void a();} 5.接口定义的方法的权限修饰默认是public,实现接口的类重写其方法时,必须显示声明public修饰:6.实现接口的类

Java学习笔记_22_Set接口的实现类

22.Set接口的实现类: Set接口存放的元素是无序的且不包括反复元素. 1>实现类HashSet: HashSet类依据元素的哈希码进行存放,取出时也能够依据哈希码高速找到.HashSet不保存元素的加入的顺序. 样例: import java.util.HashSet; import java.util.Iterator; public class Student { public static void main(String[] args) { HashSet<String>

python学习笔记(接口自动化框架)

之前是利用python自带的unittest测试框架 这次自己设计一个 之后再一点点往里面加功能 (ps:当然这个框架真的是很简单..很简单...很简单...) excel文件格式: 1 #!/usr/bin/env python 2 # -*- coding: utf_8 -*- 3 4 import xlrd 5 import json 6 7 8 class CreateExcel: 9 def __init__(self): 10 pass 11 12 @classmethod 13 d

Groovy学习笔记-实现接口

1.单个委托方法的实现 button.addActionListener( { println 'Implement ActionListener' } as ActionListener ) 2.实现接口中的多个方法:使用映射,以每个方法的名字作为键,以方法对应的代码块作为键值,使用:分割方法名和代码块 handleFocus = [ focusGained : {msgLabel.setText("Good to see you")}, focusLost : {msgLabel.