一 枚举器和可枚举类型

当我们为数组使用foreach语句时,这个语句为我们依次取出了数组中的每一个元素。

var arrInt = new int[] { 11, 12, 13, 14 };
foreach (var item in arrInt)
{
Console.WriteLine(item);
}

原因是数组实现了IEnumerable接口,接口提供了一个GetEnumerator方法可以获取一个实现了IEnumerator接口的枚举器对象。

枚举器可以依次返回请求的数组中的元素。

实现了IEnumerable接口类型叫做可枚举类型。数组是可枚举类型。

public interface IEnumerable
{
// 摘要: 返回循环访问集合的枚举器。
// 返回结果:一个可用于循环访问集合的 System.Collections.IEnumerator 对象。
IEnumerator GetEnumerator();
}

foreach语句设计用来和可枚举类型一起使用。只要给它的遍历对象是可枚举类型,它就会执行如下行为:

  • 通过调用GetEnumerator方法获取对象的枚举器;
  • 从枚举器中请求每一项并把它作为迭代变量。

二 IEnumerator接口

IEnumerator接口的定义如下:

public interface IEnumerator
{
// 摘要:获取集合中位于枚举数当前位置的元素
// 返回结果:集合中位于枚举数当前位置的元素。
object Current { get; }

// 摘要:将枚举数推进到集合的下一个元素。
// 返回结果:如果枚举数已成功地推进到下一个元素,则为 true;如果枚举数传递到集合的末尾,则为 false。
// 异常:T:System.InvalidOperationException:创建枚举器后,已修改该集合。
bool MoveNext(); // 摘要:将枚举数设置为其初始位置,该位置位于集合中第一个元素之前。
// 异常: T:System.InvalidOperationException: 创建枚举器后,已修改该集合。
void Reset();
}

有了枚举器,就可以使用MoveNext和Current来模仿foreach循环:

int[] arrInt = new int[] { 11, 12, 13, 14 };
foreach (var item in arrInt)
{
Console.WriteLine(item);
} var enumerator = arrInt.GetEnumerator();
while (enumerator.MoveNext())
{
var item = (int)enumerator.Current;
Console.WriteLine(item);
}

下面是一个使用IEnumerator和IEnumerable的小例子:

class Program
{
static void Main(string[] args)
{
var myColor = new MyColors();
foreach (var item in myColor)
{
Console.WriteLine(item);
}
}
} class ColorEnumeraotr : IEnumerator
{
string[] _colors;
int _position = -1; public ColorEnumeraotr(string[] theColors)
{
_colors = new string[theColors.Length];
for (int i = 0; i < theColors.Length; i++)
{
_colors[i] = theColors[i];
}
} public object Current
{
get
{
if (_position == -1)
throw new InvalidOperationException();
if (_position >= _colors.Length)
throw new InvalidOperationException(); return _colors[_position];
}
} public bool MoveNext()
{
if (_position < _colors.Length - 1)
{
_position++;
return true;
}
return false;
} public void Reset()
{
_position = -1;
}
} class MyColors : IEnumerable
{
string[] Colors = new string[] { "red","blue","yellow","green","white"}; public IEnumerator GetEnumerator()
{
return new ColorEnumeraotr(Colors);
}
}

三 泛型枚举接口

非泛型接口的实现不是类型安全的,它返回object的引用,然后必须转化为实际类型。

所以实际上,在多数情况下,我们应该使用泛型版本的IEnumerable<T>和IEnumerator<T>。

泛型接口的枚举器是类型安全的,它返回实际类型的引用。

IEnumerable<T>接口的GetEnumerator方法返回实现IEnumerator<T>的枚举器对象。

实现IEnumerator<T>的枚举器类的Current属性,它返回T类型的对象。

四 迭代器

C#从2.0版本开始提供了更简单的创建枚举器和可枚举类型的方式。这种结构叫做迭代器。

迭代器块是有一个或多个yield语句的代码块。

迭代器块与其他代码块不同。其他块包含的语句是命令式的,也就是块中的语句依次执行,最后离开块。

而迭代器块是描述了希望编译器为我们创建的枚举器类的行为,迭代器块中的代码描述了如何枚举元素。

迭代器块有两个特殊语句:

  • yield return : 指定了序列中返回的下一项;
  • yield break : 指定在序列中没有其他项。
//产生枚举器的迭代器
public IEnumerator<string> IteratorMethod1()
{
yield return "red";
yield return "blue";
yield return "yellow";
} //产生可枚举类型的迭代器
public IEnumerable<string> IteratorMethod2()
{
yield return "red";
yield return "blue";
yield return "yellow";
}

4.1 使用迭代器来创建枚举器

下面代码演示如何使用迭代器来创建枚举器:

class Program
{
static void Main(string[] args)
{
var mc = new MyClass();
foreach (var item in mc)
{
Console.WriteLine(item);
}
}
} class MyClass
{
public IEnumerator<string> GetEnumerator()
{
return MyEnumerator(); //返回枚举器
} //迭代器
public IEnumerator<string> MyEnumerator()
{
yield return "red";
yield return "blue";
yield return "yellow";
}
}

4.2 使用迭代器来创建可枚举类型

下面代码演示如何使用迭代器来创建可枚举类型:

class Program
{
static void Main(string[] args)
{
var mc = new MyClass();
foreach (var item in mc)
{
Console.WriteLine(item);
} foreach (var item in mc.MyEnumerable())
{
Console.WriteLine(item);
}
}
} class MyClass
{
public IEnumerator<string> GetEnumerator()
{
IEnumerable<string> myEnumerable = MyEnumerable();
return myEnumerable.GetEnumerator();
} public IEnumerable<string> MyEnumerable()
{
yield return "red";
yield return "blue";
yield return "yellow";
}
}

最新文章

  1. 迭代器学习之一:使用IEnumerable和IEnumerator接口
  2. AC日记——字符串P型编码 openjudge 1.7 31
  3. hadoop问题锦集(一)-搭建集群环境时的常见问题
  4. 微软职位内部推荐-Sr SDE-MODC-Beijing
  5. 百度SEO优化
  6. css学习笔记四
  7. cocos2dx进阶学习之屏幕适配
  8. Part Acquisition(spfa输出路径)
  9. spring框架总结(03)重点介绍(Spring框架的第二种核心掌握)
  10. Springboot的默认定时任务——Scheduled注解
  11. JDK1.5以后的版本特性
  12. 19.职责链模式(Chain of Responsibility Pattern)
  13. windows下KafkaOffsetMonitor下载及安装
  14. css文本属性用法总结
  15. J S 脚本语言 if() { if { } else { } } var a =100; switch { case ( ) break ; } 基础详解 , 最下面有例子
  16. Windows Live Wirter
  17. Docker基础速成(一)
  18. Java内存模型(JMM)以及 垃圾回收机制 小结
  19. perl6 登录phpmyadmin
  20. [置顶] 【机器学习PAI实践七】文本分析算法实现新闻自动分类

热门文章

  1. 日志审计与分析实验三(rsyslog服务器端和客户端配置)(Linux日志收集)
  2. 2022-07-21 第四组 java之继承
  3. 二分法求最长子序列长度(STL)(nlogn)
  4. javascript自执行函数表达式
  5. mybatis 01: 静态代理 + jdk动态代理
  6. Luogu2801 教主的魔法 (分块)
  7. Codeforces 1715E - Long Way Home
  8. 1.1_selenium+Python自动化测试大纲
  9. Java控制台打印三角形
  10. iOS WebRTC 点对点实时音视频流程介绍