foreach/yield语句自定义IEnumerable和IEnumerator类型
2015-10-02 09:33
465 查看
IEnumerable和IEnumerator类型:
IEnumerable只是一个接口用于foreach迭代器遍历之前用一个GetEnumerator获取一个IEnumerator对象且只获取一次,IEnumerator才是真正的迭代器对象,IEnumerator有Current属性,MoveNex()t和Reset()方法。所以如果是系统默认类型的IEnumerable<T>和IEnumerator<T>那么就不需要自定义这两个类了,反之则要定义。这个是一个迭代器模式,是C#广泛使用泛型和面向接口编程的经典体现。
含有数组成员的类会继承Array类,Array类会继承IEnumerable类,IEnumerable类包含了一个IEnumerator GetEnumerator() 函数,foreach语句会利用返回的IEnumerator的Current,MoveNext(),Reset()来循环处理函数。
例如:
foreach( var p in persons )
{
Console.WriteLine(p);
}
会被CRL编译为:
IEnumerator<Person> enumerator = persons.GetEnumerator();
while(enumerator.MoveNext())
{
Person p = enumerator.Current;
Console.WriteLine(p);
}
所以要声明支持foreach类型的自定义枚举集合类,那么需要声明数组默认继承IEnumerable需要实现IEnumerator<T> GetEnumerator()函数,再定义自己的IEnumerator<T>类,如果是默认类型的IEnumerator<T>就不需要了,这里只是用了迭代器设计模式和面向接口的编程思想,例如:
来自:https://msdn.microsoft.com/zh-cn/library/system.collections.ienumerable.getenumerator%28v=vs.110%29.aspx 的实例
2.yield语句
yield语句也是一个迭代块,必须声明返回值类型为IEnumerable、IEnumerable<T>、IEnumerator<T>、IEnumerator类型,如果非内置类型的IEnumerable<T>还要定义IEnumerator<T> GetEnumerator(), 如果IEnumerator是非内置类型的那么需要定义真正迭代器的Current属性,和MoveNext,Reset方法。
这样yield语句会生成一个枚举器IEnumerator,CRL翻译为Switch语句的MoveNext、Current语句、State保持当前的语句下一个索引状态的状态迭代器,下次重入的时候就从迭代器的下一个索引取数据并置下一个状态,就马上返回,就是一个很简单程序设置没有什么高级的分段执行机制,只是在多线程异步事件或者协程事件中能够发挥强大的作用。
IEnumerable只是一个接口用于foreach迭代器遍历之前用一个GetEnumerator获取一个IEnumerator对象且只获取一次,IEnumerator才是真正的迭代器对象,IEnumerator有Current属性,MoveNex()t和Reset()方法。所以如果是系统默认类型的IEnumerable<T>和IEnumerator<T>那么就不需要自定义这两个类了,反之则要定义。这个是一个迭代器模式,是C#广泛使用泛型和面向接口编程的经典体现。
1.foreach语句
C#中的foreach语句不会翻译为IL中的foreach语句,而是会翻译为IEnumerable中的接口的属性和函数,将类型替换为相应的泛型,IEnumerator<T> while MoveNext Current语句,会用while一次遍历迭代器的所有元素。含有数组成员的类会继承Array类,Array类会继承IEnumerable类,IEnumerable类包含了一个IEnumerator GetEnumerator() 函数,foreach语句会利用返回的IEnumerator的Current,MoveNext(),Reset()来循环处理函数。
例如:
foreach( var p in persons )
{
Console.WriteLine(p);
}
会被CRL编译为:
IEnumerator<Person> enumerator = persons.GetEnumerator();
while(enumerator.MoveNext())
{
Person p = enumerator.Current;
Console.WriteLine(p);
}
所以要声明支持foreach类型的自定义枚举集合类,那么需要声明数组默认继承IEnumerable需要实现IEnumerator<T> GetEnumerator()函数,再定义自己的IEnumerator<T>类,如果是默认类型的IEnumerator<T>就不需要了,这里只是用了迭代器设计模式和面向接口的编程思想,例如:
来自:https://msdn.microsoft.com/zh-cn/library/system.collections.ienumerable.getenumerator%28v=vs.110%29.aspx 的实例
using System; using System.Collections; // Simple business object. public class Person { public Person(string fName, string lName) { this.firstName = fName; this.lastName = lName; } public string firstName; public string lastName; } // Collection of Person objects. This class // implements IEnumerable so that it can be used // with ForEach syntax. public class People : IEnumerable { private Person[] _people; public People(Person[] pArray) { _people = new Person[pArray.Length]; for (int i = 0; i < pArray.Length; i++) { _people[i] = pArray[i]; } } // Implementation for the GetEnumerator method. // People继承自IEnumerable,用于该类获取真正的迭代器 IEnumerator IEnumerable.GetEnumerator() { return (IEnumerator)GetEnumerator(); } // PeopleEnum为真正的迭代器,因为PeopleEnum是自定义了,默认不支持 // 所以需要为PeopleEnum迭代器定义Current属性,和MoveNext,Reset方法。 public PeopleEnum GetEnumerator() { return new PeopleEnum(_people); } } // When you implement IEnumerable, you must also implement IEnumerator. public class PeopleEnum : IEnumerator { public Person[] _people; // Enumerators are positioned before the first element // until the first MoveNext() call. // 构造时候会初始化这些 int position = -1; public PeopleEnum(Person[] list) { _people = list; } // MoveNext为false就不会继续移动了,除非reset或重新生成一个 public bool MoveNext() { position++; return (position < _people.Length); } public void Reset() { position = -1; } // 真正的迭代器要求定义的属性,会调用Person Current object IEnumerator.Current { get { return Current; } } // 当前的获取Current的值 public Person Current { get { try { return _people[position]; } catch (IndexOutOfRangeException) { throw new InvalidOperationException(); } } } } class App { static void Main() { Person[] peopleArray = new Person[3] { new Person("John", "Smith"), new Person("Jim", "Johnson"), new Person("Sue", "Rabon"), }; // 自定义的IEnumerable类型 People peopleList = new People(peopleArray); // 会得到自定义的PeopleEnum,接着进行循环遍历 foreach (Person p in peopleList) Console.WriteLine(p.firstName + " " + p.lastName); } } /* This code produces output similar to the following: * * John Smith * Jim Johnson * Sue Rabon * */
2.yield语句
yield语句也是一个迭代块,必须声明返回值类型为IEnumerable、IEnumerable<T>、IEnumerator<T>、IEnumerator类型,如果非内置类型的IEnumerable<T>还要定义IEnumerator<T> GetEnumerator(), 如果IEnumerator是非内置类型的那么需要定义真正迭代器的Current属性,和MoveNext,Reset方法。这样yield语句会生成一个枚举器IEnumerator,CRL翻译为Switch语句的MoveNext、Current语句、State保持当前的语句下一个索引状态的状态迭代器,下次重入的时候就从迭代器的下一个索引取数据并置下一个状态,就马上返回,就是一个很简单程序设置没有什么高级的分段执行机制,只是在多线程异步事件或者协程事件中能够发挥强大的作用。
相关文章推荐
- hdu 1098Ignatius's puzzle(math)
- Android目录(更新中)
- SCI&EI 英文PAPER投稿经验【转】
- JavaScript权威指南学习之第2章 词法结构
- windows对象总结
- less语法基础
- Spring MVC一些配置信息解析
- java垃圾回收总结(2)
- 利用imagettftext实现动态的促销文字的变换
- Java基础知识强化之集合框架笔记14:List集合存储字符串并遍历
- Linux下静态库和动态库的创建和使用
- java 垃圾回收总结(1)
- 程序设计基石与实践之C语言内存地址基础
- WM_COMMAND 和 WM_NOTIFY 的区别
- 弹性盒子(flexible box)
- 字符串是否包含问题及扩展
- MarkDown 认识与入门
- 可移植性
- PHP中heredoc的用法(注意事项!)
- Linux的system()和popen()差异 http://blog.csdn.net/liuxingen/article/details/47057539