迭代器学习之三:IEnumerable和IEnumerator的泛型结构
上一篇讲了关于数组可枚举类型和枚举数的实现,这一片会涉及到IEnumerable和IEnumerator的泛型结构!
在讲之前,首先大家要对C#的泛型知识要有一定的理解,如果想学习,可以参考其它的资料学习!
泛型的优点大家都知道,不仅是类型安全,还在扩展性方面变的很强大,以及在性能上的优势也是有目共睹的,废话不多说,Let's go。
1. IEnumerable<T>接口
首先来看一下它的泛型接口定义:
1 public interface IEnumerable<out T> : IEnumerable
2 {
3 // Methods
4 IEnumerator<T> GetEnumerator();
5 }
可以看得出这个泛型接口还继承了它的非泛型接口,示例如下:
1 public class My : IEnumerable<string>
2 {
3 //①
4 public IEnumerator<string> GetEnumerator()
5 {
6 //返回实现IEnumerator<T>接口的类型
7
8 //throw new NotImplementedException();
9 }
10
11 //②
12 IEnumerator IEnumerable.GetEnumerator()
13 {
14 //返回实现IEnumeratro接口的类型,这是显示实现继承非泛型IEnumerable接口的方法
15
16 //throw new NotImplementedException();
17 }
18 }
从上面的例子中可以看出实现IEnumerable<T>接口会自动的实现非泛型接口中的方法,所以在调用时如果你想返回泛型的枚举数就调用返回值为泛型IEnumerator<T>的方法,反之调用非泛型的,其实调用哪个具体是看对象的实例类型是什么!
★ 如果这样写:IEnumerable my = new My (); ,那么这个实例就会调用 IEnumerable.GetEnumerator()方法,因为这个实例的类型为非泛型的IEnumerable接口,而且这个方法也规定只有是IEnumerable类型的实例才可以调用,但是如果My这个类型是个泛型,那么就会设计到装箱和拆箱的性能问题了!
对于上面定义的类还有更好的写法,也就是把这个类也定义为泛型,如下:
1 public class My<T> : IEnumerable<T>
2 {
3 #region IEnumerable<T> Members
4
5 public IEnumerator<T> GetEnumerator() //这样就使得扩展性能更加强大了
6 {
7 throw new NotImplementedException();
8 }
9
10 #endregion
11
12 #region IEnumerable Members
13
14 IEnumerator IEnumerable.GetEnumerator()
15 {
16 throw new NotImplementedException();
17 }
18
19 #endregion
20 }
这样定义之后这个类的扩展性就很好了,下面看看IEnumerator<T>的泛型结构!
2.IEnumerator<T>接口
还是先看下它的定义:
① 通过上图可以看出不仅继承了IEnumerator接口,还继承了IDisposable接口,我想继承IDisposable这个接口有可能是因为当我泛型是一个占用系统资源的对象时,那么我们就很容易来释放它!
② 还有就是把Current属性的类型由object类型变为了T,这样就充分的利用了泛型的优点,避免的装箱和拆箱的操作!
代码如下:
1 public class My : IEnumerator<string>
2 {
3 //返回类型跟接口泛型类型一致
4 public string Current
5 {
6 get { throw new NotImplementedException(); }
7 }
8
9 //增加了释放资源的方法
10 public void Dispose()
11 {
12 throw new NotImplementedException();
13 }
14
15 //这边不可以加访问修饰符的(显式实现接口方法),不理解可以看看接口继承这一类的文章
16 object IEnumerator.Current //这边属性默认私有的,不好修改为public
17 {
18 get { throw new NotImplementedException(); }
19 }
20
21 public bool MoveNext() //移动到下一项的方法
22 {
23 throw new NotImplementedException();
24 }
25
26 public void Reset() //重置
27 {
28 throw new NotImplementedException();
29 }
30 }
3.综合示例
1 //这是可枚举类型
2 public class MyGenericsIEnumerable<T> : IEnumerable<T>
3 {
4 private List<T> _list = new List<T>();
5
6 public IEnumerator<T> GetEnumerator() //返回泛型的枚举数
7 {
8 //return this._list.GetEnumerator(); //调用集合的GetEnumerator方法,获取它的可枚举数
9 return new MyGenericsIEnuerator<T>(this._list);
10 }
11
12 IEnumerator System.Collections.IEnumerable.GetEnumerator()
13 {
14 //return this._list.GetEnumerator();
15 return new MyGenericsIEnuerator<T>(this._list);
16 }
17
18 public void MyAdd(T t)
19 {
20 this._list.Add(t);
21 }
22 }
23
24 //对枚举数进行遍历
25 public class MyGenericsIEnuerator<T> : IEnumerator<T>
26 {
27 int _position = -1;
28 private List<T> _list = null;
29
30 public MyGenericsIEnuerator(List<T> list)
31 {
32 this._list = list;
33 }
34
35 public T Current
36 {
37 get { return this._list[this._position]; }
38 }
39
40 public void Dispose() //释放资源
41 {
42 GC.Collect(); //回收垃圾
43 }
44
45 object System.Collections.IEnumerator.Current //注意这个方法,只有是IEnumerator的实例才能调用
46 {
47 get { return this._list[this._position]; }
48 }
49
50 public bool MoveNext()
51 {
52 if (this._position < this._list.Count - 1)
53 {
54 this._position++;
55 return true;
56 }
57 else
58 { return false; }
59 }
60
61 public void Reset()
62 {
63 this._position = -1;
64 }
65 }
66
67 public class People
68 {
69 public string Name { get; set; }
70
71 public int Age { get; set; }
72 }
客户端代码调用如下:
1 //客户端调用2
3 public static void Main(string[] args)
4 {
6 //泛型类型为People对象
7
8 MyGenericsIEnumerable<People> myEnumerable = new MyGenericsIEnumerable<People>();
9 myEnumerable.MyAdd(new People() { Name = "One", Age = 10 });
10 myEnumerable.MyAdd(new People() { Name = "Two", Age = 20 });
11 myEnumerable.MyAdd(new People() { Name = "Three", Age = 30 });
12 foreach (People item in myEnumerable)
13 {
14 Console.WriteLine(item.Name + "\t" + item.Age.ToString());
15 }
19 //看一个比较少见的调用
21 Console.WriteLine("----------------------------------------------------------------");
22 IEnumerable myNine = myEnumerable; //这样就会调用IEnumerable.GetEnumerator()方法
23 foreach (People item in myNine)
24 {
25 Console.WriteLine(item.Name + "\t" + item.Age);
26 }
运行结果:
总结:在foreach遍历时,会调用MyGenericsIEnumerable泛型类中的GetEnumerator方法(是实现泛型的那个方法),然后返回一个枚举数对象,最后再到MyGenericsIEnuerator泛型类中调用方法返回每一项的值(其实上面的例子返回的是一个People对象),最终呈现的界面上!
最后声明一点:在最后检查的时候发现了一个很严重的知识误区!
① 注意请把高亮显示的代码跟上面用红色的代码比较一下()
a)如果使用红色代码,那么它就会返回一个List自己定义的一个枚举数类型,最后再调用List里面自己定义的方法返回每一项,如果这样那我下面自己定义的枚举数类型不就没用了,想到这我知道自己陷入了一个误区了。
b)首先我这边定义的List集合是用来存储数据的,而不是直接返回List自己定义的枚举数类型,应该返回的是自己定义的枚举数类型
c)在返回自己的枚举数类型时候大部分情况是通过在构造函数里面进行数据的传输(.NET中大部分也是这样的)
② 最后大家应该好好理解自己定义的和.NET定义的可枚举类型和枚举数,不然会搞的很乱!
本人新手,路过的留下对本文的意见,我会不断改善的,菜鸟需要你们的指导,才能飞的更远!
在学习中深入,在实践中提高,最近在做unit test,烦的很,不过还是要Come on!
下一篇:迭代器学习之四:yield迭代器的深入理解