实现IEnumerator 已支持foreach
来源:互联网 发布:写安卓软件 编辑:程序博客网 时间:2024/06/10 08:46
本文严重参考了 笔记:IEnumerable和IEnumerator(包括泛型版), 另外caozhy在BBS也有一段总结(链接地址), 可以参考
一个GetEnumerator()方法, 无参数,返回类型任意
Current属性,只有get方法,不能有set方法(编译器也不允许),返回类型任意
MoveNext()方法,无参数, 判断是否到遍历完毕,返回类型bool
为了让Current属性知道应该返回哪个值了, 和 辅助MoveNext判断是否遍历完毕, 通常还会有个 int index, 虽然不必须但很有必要
其实接口的本质就是让编译器去绑定一个对象的方法,在这里,C#编译器的确不依赖接口
//手动让一个int数组支持foreach class A { int[] array = new int[]{1,2,3,4,5}; int index=-1; public A GetEnumerator() { return this; } public int Current { get { return array[index]; } } public bool MoveNext() { index++; return index < array.Length ? true : false; } } class TaskThread { static void Main() { A a = new A(); foreach (var s in a) Console.WriteLine(s); } }
IEnumerator接口指明需要Current、MoveNext(), 但VS中点击 "实现接口", 通常还会生成Reset()方法.
一般我们对一个数组变量连续2次使用 foreach, 就会循环两遍; 如果你仅实现了上面的三个方法和属性, 连续2次foreach却不能达到要求
class A { int[] array = new int[]{1,2,3,4,5}; int index=-1; public A GetEnumerator() { return this; } public int Current { get { return array[index]; } } public bool MoveNext() { index++; if (index < array.Length) return true; else { //Reset(); return false; } } //private void Reset() //{ // index = -1; //} } class TaskThread { static void Main() { A a = new A(); foreach (var s in a) Console.WriteLine(s); Console.WriteLine("循环第二次"); foreach (var s in a) Console.WriteLine(s); } }
当把注释掉的 Reset() 方法恢复后, 就可以得到正常结果了.
4. IEnumerable和IEnumerator通过IEnumerable的GetEnumerator()方法建立了连接,可以通过IEnumerable的GetEnumerator()得到IEnumerator对象
那为什么集合类不直接继承(支持)IEnumerator<T>和IEnumerator接口?
假如同时有两个循环交错遍历同一个集合(一个foreach嵌套了另外一个foreach),那么集合必须维持当前元素的一个状态指示器,确保当调用MoveNext()方法时,能正确定位下一个元素。
为了解决这个问题,集合类不直接支持IEnumerator<T>和IEnumerator接口。而是通过IEnumerable的GetEnumerator返回一个新的IEnumerator对象来负责维护循环遍历的状态,IEnumerator(迭代器,或者叫枚举数)相当于一个“游标”(cursor)或者“书签”。可以有多个书签,移动每个书签都可独立于其他书签来遍历集合
下面的代码是双线程同时访问集合
class A { int[] array = new int[]{1,2,3,4,5}; int index=-1; public A GetEnumerator() { return this; } public int Current { get { return array[index]; } } public bool MoveNext() { index++; if (index < array.Length) return true; else { Reset(); return false; } } private void Reset() { index = -1; } } class TaskThread { static A a = new A(); static void Main() { Thread m = new Thread(GetSum); Thread n = new Thread(GetSum); m.Start(); n.Start(); } static void GetSum() { int sum = 0; foreach (var s in a) { Thread.Sleep(100); //模拟大量运算, 否则看不出效果 sum += s; } Console.WriteLine("ThreadID={0}, sum={1}",Thread.CurrentThread.ManagedThreadId,sum); }
运行的结果是
ThreadID=3, sum=5
ThreadID=4, sum=26
按理正常结果应该是 15, 可以看出两个线程都分别干扰了对方的index
5. 由于IEnumerable<T>扩展(继承)了旧的IEnumerable接口,所以要实现两个不同的方法:
IEnumerator<T> GetEnumerator();
IEnumerator IEnumerable.GetEnumerator; // 由于和泛型版本的方法同名,所以该方法的实现需要使用显式接口实现
6. 我们通过ILSpy 看看源码中 List 的IEnumerator定义
public struct Enumerator : IEnumerator<T>, IDisposable, IEnumerator{private List<T> list;private int index;private int version;private T current;[__DynamicallyInvokable]public T Current{[__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]get{return this.current;}}[__DynamicallyInvokable]object IEnumerator.Current{[__DynamicallyInvokable]get{if (this.index == 0 || this.index == this.list._size + 1){ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);}return this.Current;}}internal Enumerator(List<T> list){this.list = list;this.index = 0;this.version = list._version;this.current = default(T);}[__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]public void Dispose(){}[__DynamicallyInvokable]public bool MoveNext(){List<T> list = this.list;if (this.version == list._version && this.index < list._size){this.current = list._items[this.index];this.index++;return true;}return this.MoveNextRare();}private bool MoveNextRare(){if (this.version != this.list._version){ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);}this.index = this.list._size + 1;this.current = default(T);return false;}[__DynamicallyInvokable]void IEnumerator.Reset(){if (this.version != this.list._version){ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);}this.index = 0;this.current = default(T);}}
- 实现IEnumerator 已支持foreach
- 实现IEnumerable和IEnumerator接口以支持foreach语句
- 如何自己实现IEnumerable和IEnumerator接口以支持foreach语句
- 一个类型要想支持foreach则必须实现IEnumerable,IEnumerator两个接口。
- 如何自己实现IEnumerable和IEnumerator接口以支持foreach语句
- c#实现Foreach枚举_IEnumerable和IEnumerator
- 迭代器.NET实现—IEnumerable和IEnumerator (foreach实现)
- foreach与IEnumerator
- 读书笔记:关于foreach,IEnumerable,IEnumerator,CollectionBase
- Linq与where实现查询(Linq to Entity)【IEnumerable与IEnumerator与IList】|自己实现foreach的功能
- c#中foreach与接口IEnumerator和IEnumerable
- C#中的IEnumerable、IEnumerator与foreach深入探讨
- foreach/yield语句自定义IEnumerable和IEnumerator类型
- c#中foreach与接口IEnumerator和IEnumerable
- C#中实现类型对foreach的支持
- C# 支持foreach
- ie8支持foreach
- VB.NET中实现IEnumerator接口
- HDU 2614 Beat (DFS)
- URI, URL, URN
- yii2 多数据库操作
- bootstrap v3学习笔记之JavaScript插件
- "ALT+/" eclipse快捷键失效问题
- 实现IEnumerator 已支持foreach
- Android Studio实战技巧
- 37. Element appendChild() 方法
- android 5.0新特性学习总结之下拉刷新(一)
- ASP.Net在web.config中设置上传文件的大小方法 (转)
- 诚信
- Spring @Required注释
- IOS TableView详解(二)
- 学PHP那点事?