自定义类型实现系统接口(一)

来源:互联网 发布:凡客建站seo 编辑:程序博客网 时间:2024/05/17 02:28

 最近在看垃圾回收的IDispose的接口实现,平时经常听到某个类型要执行什么操作需要实现什么接口,某个类型实现了什么接口。但我们用的系统提供的类型都已经实现了这些接口,但到底怎么实现的我们不清楚。所以我们就把这些常用的接口实现在自己的类型中。让他们具有一定功能,也让我认识下这些接口。

下面的例子中一共实现了IComparable,IComparer,IEnumerable,IEnumerator和IDisposable,实现这些接口,我们的类型就可以在List或Array中进行排序,使用foreach进行遍历,手动释放资源或使用using块。在看代码前先说明下,2.0中增加了泛型的支持,所以也有了泛型的接口。我下面说的主要是针对接口,而不管是不是泛型(区别不大),但代码中都是实现的泛型接口。所以看到介绍是非泛型,代码是泛型不要误会,我主要是介绍接口以及实现。

 

1:实现IComparable,IComparer接口

IComparable:定义由值类型或类实现的通用的比较方法,以为排序实例创建类型特定的比较方法。

IComparer:定义类型为比较两个对象而实现的方法。

通常我们要在数组或链表中对一个对象进行排序,这就需要对对象进行比较。但因为数组和链表可以存放各种数据,值类型,引用类型,或是我们自建类型,所以它不可能去实现各个类型的比较方法,而需要类型自己实现。所以,.NET中如果一个类型实现了IComparable接口,那么他就可以被排序,因为排序算法知道去调用这个类型的CompareTo方法。

但是有时候我们类型有多个字段,需要能选择的进行排序,这个时候就需要在此类型中实现IComparer接口,以便按自己的方式进行排序。如果不实现此接口,系统会自己产生一个默认的实现。但是要注意的是2个接口的实现不是直接在一个类上,而是通过嵌套类。

 

 

2:实现IEnumerable,IEnumerator接口

IEnumerable:公开枚举数,该枚举数支持在指定类型的集合上进行简单迭代。

IEnumerator:支持非泛型集合上进行简单迭代。

这2个接口是为我们的类型提供可枚举迭代的功能,也就是我们可以用foreach来访问我们的类型中的数据。其中IEnumerable只是返回一个枚举集合,表示这个类型是一个可枚举的,而要要实现迭代功能的是要通过IEnumerator实现的。下面是1.1和2.0中不同的实现。

在2.0中增加yield关键字(代码中yield竟然不是蓝色,看来CSDN还没到2.0啊,哈哈), 所以我们自己只需要实现IEnumerable接口,yield关键字为我们自动实现了IEnumerator接口。大家可以编译1.1和2.0的方法,然后使用ILDASM查看。

 

这里是.NET1.1中的实现,我们需要自己是实现迭代的过程,方法是在克枚举类型中定义一个内嵌类,来实现IEnumerator接口,实现具体的迭代关系。

 

 

3:实现IDisposable接口

IDisposable:定义一种释放分配的非托管资源的方法

法。看到下面的类并没有指定 :IDisposable但可以正常运行,而上面的例子如果去掉就会提示没有实现XX接口....。这个主要和调用有关的,上面类型,比如排序的时候调用比较方法是通过接口类型来调用的,而非类型本身的 类型。而在释放资源时是通过本类型来调用的。但还是建议指定继承与接口,这样就可以强制你必须实现这个功能。否则你无法使用useing块。

Dispose,close,Finalize是我们常见的3中资源回收的方法,其中FinaliFinalize是CLR的回收机制,他主要是用来回收本地非托管资源(文件句柄,图像资源等),无法控制执行,我们在类型中使用~类名()这样的形式来定义Finalize,这个和C++的析构函数很象,但原理完全不一样,他是系统在垃圾回收或CLR关闭等情况下被调用的。但有些时候我们知道资源已经用完,比如文件已经写入,这个时候我们就可以手动回收资源,而不要垃圾回收期回收,以免垃圾回收器的代龄被提高。Dispose是我们提供的一个显式的释放资源的方法。而Close方法和Dispose实现是一样的。一般实现了Finalize的都需要实现Dispose释放模式,但实现了释放模式的不一定需要实现Finalize。最后要注意的是Dispose和Finilaze方法中不要引用其他类型的的释放或终结方法,因为总结方法调用是系统决定的,先后顺序是不定的,所以容易出现错误。

 

以上是使用我们自己的类型实现了这些接口,下面就可以看下这些接口给类型带来的实际效果。

 

 

 

我们看到了2个版本的实现效果完全一样,而且都可以使用foeach或枚举对象的方法来访问。当使用枚举对象时我们可以用泛型或非泛型来操作。不同大家应该可以看的到,非泛型是Object,不能直接调用test类型的print方法,需要强制转化。所以在性能上泛型接口还是要好一些。但是我们程序中实现的是泛型接口而不是非泛型的啊。观察类型实现代码可以发现,我们实现了泛型和非泛型2种方法,这个是系统要求的,是未来确保兼容性。因为泛型接口实际也是继承与非泛型接口的。

 

 

 

最后一个就是垃圾回收的情况了,分别演示了手动回收,对已回收的对象进行回收,使用垃圾回收器自动回收,和使用using块。这里注意的时使用using块必须实现IDisposable,否则系统提示 using 语句中使用的类型必须可隐式转换为"System.IDisposable”。代码中GC.collect()是为了演示,实际上实现了Dispose我们是不需要手动进行垃圾回收的。完全由垃圾回收器自己进行。

 

 

 

 

 

我们可以看前面类型中有本地非托管资源和托管资源。使用Dispose,Close和using块效果是一样的,对托管和非托管的资源进行回收。而对一个已回收的对象资源在进行回收是没有效果的。这里也可以看到,对资源进行释放并没有把对象从GC堆上删除。要明白的是所有的Dispose,Finilaze操作都只是回收资源。而不是释放内存空间。任何对象的内存空间,都是CLR进行垃圾回收的。我们显示释放资源,只是为了对象能尽早的被垃圾回收,以免提高代龄(因为垃圾回收时,如果对象引用了其他的资源,需要首先回收资源,下一次垃圾回收才进行内存回收。而资源释放是系统控制的,如果不手动释放,可能会增长垃圾对象在GC堆中的时间,甚至发生复苏或提高代龄)。

源代码下载地址:http://download.csdn.net/source/797668

 

 

 

类型实现了可被排序的功能,其中不传递排序字段时是按默认比较器排序的,而如果指定了,则是按自己的实现来排序。而指定时是通过生成嵌套的迭代类的实例来指示要排序的字段.

 

 

 

原创粉丝点击