基础【循环】-----(枚举器)------(转)

Iterator:枚举器

如果你正在创建一个表现和行为都类似于集合的类,允许类的用户使用foreach语句对集合中的成员进行枚举将会是很方便的。这在C# 2.0中比 C# 1.1更容易实现一些。作为演示,我们先在 C# 1.1中为一个简单的集合添加枚举,然后我们修改这个范例,使用新的C#2.0 枚举构建方法。

我们将以创建一个简单化的List Box作为开始,它将包含一个8字符串的数组和一个整型,这个整型用于记录数组中已经添加了多少字符串。构造函数将对数组进行初始化并使用传递进来的参数填充它。

1 public ListBox(params string[] initialStrings)
2  {
3      strings = new String[8]; 
4 
5     foreach (string s in initialStrings)
6      {
7         strings[ctr++] = s;
8      }
9  }
View Code

除此以外,ListBox类还需要一个Add方法(进行添加 string 的操作) 和 一个返回数组中字符串个数的方法。

 1 public void Add(string theString)
 2  {
 3      strings[ctr] = theString;
 4      ctr++;
 5  }
 6  
 7 public int GetNumEntries()
 8  {
 9      return ctr;
10  }
View Code

NOTE:实际开发中,通常使用ArrayList,而不是固定大小的数组。在这里为了程序简单就没有做数组下标越界的检测。

从感觉上看,ListBox像是一个集合,如果可以使用集合中通常使用的 foreach 循环来获取listBox中的所有字符串将会是非常便利的。如此的话,可以这样书写代码:

1 ListBox lb = new ListBox("a", "b", "c", "d", "e", "f", "g", "h");
2  foreach (string s in lb) {
3      Console.WriteLine(s);
4  }
View Code

但是,会得到这样一个错误:

“Iterator.ListBox”不包含“GetEnumerator”的公共定义,因此 foreach 语句不能作用于“Iterator.ListBox”类型的变量

  

1 public IEnumerator GetEnumerator()
2  {
3      return new ListBoxEnumerator();
4  }

现在,ListBox 可以使用 foreach 循环了:

 1 ListBox lbt = new ListBox("Hello", "World");
 2  
 3 lbt.Add("Who");
 4  lbt.Add("Is");
 5  lbt.Add("John");
 6  lbt.Add("Galt");
 7  
 8 foreach (string s in lbt)
 9  {
10      Console.WriteLine("Value: {0}", s);
11  }
View Code

先是实例化这个ListBox ,并初始了两个字符串,随后又添加了四个。foreach循环接受ListBox实例,并且迭代它,依次返回字符串。输出是:

1 Hello
2 World
3 Who
4 Is
5 John
6 Galt

实现 IEnumerator 接口

注意到ListBoxEnumerator不仅需要实现IEnumerator接口,对于ListBox类它也需要一些特别了解;特别是,它必须可以获得ListBox的字符串数组并且遍历其所包含的字符串。IEnumerable 类和与其相关的 IEnumerator类之间的关系有一点微妙。实现IEnumerator接口的最好办法是在IEnumerable类里创建一个嵌套的IEnumerator类。

1 public class ListBox : IEnumerable
2  {
3      // 嵌套的私有ListBoxEnumerator类实现
4      private class ListBoxEnumerator : IEnumerator
5      {
6         // 代码实现...
7      }
8      // ListBox类的代码...
9  }
View Code

注意ListBoxEnumerator需要对它所嵌入的ListBox类的一个引用。你可以通过ListBoxEnumerator的构造函数来传递。

为了实现IEnumerator接口,ListBoxEnumerator需要两个方法:MoveNext和Reset,还有一个属性:Current。这些方法和属性的任务是创建一个状态机制,确保你可以在任何时候得知ListBox中的哪个元素是当前元素,并获得那个元素。

在这个例子中,这种状态机制是通过维护一个标明当前string的索引值来完成的,并且,你可以通过对外部类的string集合进行索引来返回这个当前的string。为了达到这个目标,你需要一个成员变量保存对于外部ListBox对象的引用,以及一个整型用于保存当前索引。

private ListBox lbt;
private int index;

每次Reset方法被调用的时候,index被置为 -1。

1 public void Reset()
2  {
3      index = -1;
4  }

每次MoveNext被调用的时候,外部类的数组检查时候已经到了末尾,如果是这样,方法返回false。如果集合中还有对象,index将增加,并且方法返回true。

 1 public bool MoveNext()
 2  {
 3      index++;
 4      if (index >= lbt.strings.Length)
 5      {
 6         return false;
 7      }else
 8      {
 9         return true;
10      }
11  }
View Code

最后,如果MoveNext方法返回True,foreach循环将调用Current属性。ListBoxEnumerator的Current属性的实现是索引外部类(ListBox)中的集合,并且返回找到的对象(这个例子中,是一个字符串)。注意,返回一个Object是因为IEnumerator接口中Current属性的签名如此。

1 public object Current
2  {
3      get {
4         return(lbt[index]);
5      }
6  }
View Code

在1.1中,所有想要通过foreach循环来迭代的类都需要实现IEnumerable接口,于是,必须创建一个实现了IEnumerator的类。最糟的是,enumerator返回的值并不是类型安全的。记得Current属性返回一个Object对象;它仅仅简单的假设你所返回的值与foreach循环所期望的相符合。

C# 2.0 的解救办法

使用C# 2.0 这些问题如同五月末的雪般融化了。在这个例子的2.0版本中,我重写上面的列表,使用C# 2.0的两个新特性:泛型 和 枚举器。

我以重新定义实现IEumerable<string>的ListBox作为开始:

public class ListBox : IEnumerable<string>

这样做确定这个类可以在foreach循环中使用,同时确保迭代的值是string类型。

现在,从上个例子中挪去整个嵌套类,并且用下面的代码替换 GetEnumerator方法。

1 public IEnumerator<string> GetEnumerator()
2  {
3     foreach (string s in strings)
4     {
5        yield return s;
6     }
7  }
View Code

GetEnumerator方法使用了新的 yield 语句。yield语句返回一个表达式。yield语句仅在迭代块中出现,并且返回foreach语句所期望的值。那也就是,对GetEnumerator的每次调用都将会产生集合中的下一个字符串;所有的状态管理已经都为你做好了!

就这样了,你已经完成了。不需要为每个类型实现你自己的enumerator,不需要创建嵌套类。你已经移除了至少30行代码,并且极大地简化了你的代码。程序继续像期望的那样运行,但是状态管理不再是你的任务,所有的都为你做好了。更进一步,由枚举器所返回的值一定是string类型,如果你想要返回其他类型,你可以修改IEnumerable泛型语句,IEnumerable泛型语句将反射新类型。

关于Yield的更多内容

作为对上一节的一些说明,应该告诉你:实际上,你可以在yield语句块中yield一个以上的值。这样,下面的语句是完全正确的C#语句:

1 public IEnumerator GetEnumerator()
2  {
3     yield return "Who";
4     yield return " is";
5     yield return "John Galt?";
6  }
View Code

假设上面的代码位于一个名为foo的类中,你可以这样写:

1 foreach ( string s in new foo())
2  {
3     Console.Write(s);
4  }

输出结果将会是:

Who is John Galt?

如果你现在停下来思考一下,这些也是之前的代码所做的事。它遍历了自己的foreach循环,并且产生出它所找到的每个string字符串。

 

转载于:https://www.cnblogs.com/shao-shao/articles/3447841.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/408265.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【论文笔记】Leveraging Line-point Consistence to Preserve Structures for Wide Parallax Image Stitching

论文链接&#xff1a;https://openaccess.thecvf.com/content/CVPR2021/papers/Jia_Leveraging_Line-Point_Consistence_To_Preserve_Structures_for_Wide_Parallax_Image_CVPR_2021_paper.pdf 代码链接&#xff1a;https://github.com/dut-media-lab/Image-Stitching 摘要 …

JTable 一个最好的例子

装载自&#xff1a;http://www.oschina.net/code/snippet_54100_1230 import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Vector;import javax.swing.DefaultCellEditor; imp…

你的QQ为谁隐身?

习惯性的打开了qq&#xff0c; 习惯性的点击了隐身选项&#xff0c; 习惯性的戴上了耳机&#xff0c; 习惯性的看看那些闪动的头像&#xff1b;        默默的&#xff0c;默默的&#xff0c;只是等待&#xff1b; 一个月&#xff0c;两个月&#xff0c;三个月…     …

一些感动的日子

http://spaces.msn.com/members/coolbug/Blog/cns!1paSXV471KCYBnh-ynIQiUkg!543.entry

bravo

大白&#xff1a; p70 放置街灯 一开始看大白上的思路硬是 没看懂 后来看代码时有几个疑点 再结合 前面的分析 豁然开朗了 beautiful mind 实在是太聪明了&#xff01;&#xff01;&#xff08;鸡冻中。。。。&#xff09; 这里的一个技巧是 要是有两个 要优化的量 v1和v2 要求…

整理javascript操作文件说明.

Javascript是网页制作中离不开的脚本语言&#xff0c;依靠它&#xff0c;一个网页的内容才生动活泼、富有朝气。但也许你还没有发现并应用它的一些更高级的功能吧&#xff1f;比如&#xff0c;对文件和文件夹进行读、写和删除&#xff0c;就象在VB、VC等高级语言中经常做的工作…

堆排序算法的实现 (冒泡排序)

方法一&#xff1a; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ConsoleApplication3 {class Program{static void Main(string[] args){List<int> seqlist new List<int> …

角色转变

11月4日花了整整一天的时间和航发锦绣家园签订了不平等“条约”时&#xff0c;开始我的角色开始准备转化为负债者&#xff0c;在之后的10年间将每月偿还给工商银行&#xffe5;849。买房&#xff0c;你说还能是什么呢&#xff1f;这个我这个年龄段的头等大事。婚姻&#xff1f;…

a href='?out=login'是什么意思

<a href?outlogin>退出</a>前面加上问号&#xff1f;就是GET方式传递outlogin是要传递的数据点这个链接就可以执行接下来通过$_GET["out"]接收这个时候$_GET["out"]的值就是login<a href?outlogin>退出</a>就是向本页传递数据要…

常用js函数

/**//*------------------------------------------------------------ dateDlg(inputid,initDate,startYear,endYear) 弹出时间窗口 compareDate(end,s) 与当前时间比较 compareTwoDate(startDate,endDate,s) 比较两个时间 isNumber(text…

读取xml文件转成ListT对象的两种方法(附源码)

读取xml文件转成List<T>对象的两种方法&#xff08;附源码&#xff09;读取xml文件&#xff0c;是项目中经常要用到的&#xff0c;所以就总结一下&#xff0c;最近项目中用到的读取xml文件并且转成List<T>对象的方法&#xff0c;加上自己知道的另一种实现方法。 就…

委托的运用

下面这篇代码展示了委托的运用,非常精彩,代码版权归"microsoft"所有. 1//版权所有 (C) 2000 Microsoft Corporation。保留所有权利。23//bookstore.cs4usingSystem;56//处理书店的一组类:7namespaceBookstore 8{ 9 using System.Collections; 10 11 // 描述图书…

visual studio enter键代码自动补全

将resharper插件中的设置内容改成这个就可以enter键自动补全了