惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Help Net Security
Help Net Security
G
Google Developers Blog
雷峰网
雷峰网
WordPress大学
WordPress大学
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Engineering at Meta
Engineering at Meta
Security Latest
Security Latest
T
Threat Research - Cisco Blogs
AWS News Blog
AWS News Blog
F
Full Disclosure
C
Cybersecurity and Infrastructure Security Agency CISA
T
The Exploit Database - CXSecurity.com
J
Java Code Geeks
U
Unit 42
C
Cyber Attacks, Cyber Crime and Cyber Security
V
V2EX
C
Cisco Blogs
博客园 - 司徒正美
Project Zero
Project Zero
L
LINUX DO - 热门话题
阮一峰的网络日志
阮一峰的网络日志
Blog — PlanetScale
Blog — PlanetScale
Scott Helme
Scott Helme
A
About on SuperTechFans
Hugging Face - Blog
Hugging Face - Blog
S
Securelist
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏
S
Schneier on Security
G
GRAHAM CLULEY
酷 壳 – CoolShell
酷 壳 – CoolShell
Cyberwarzone
Cyberwarzone
MongoDB | Blog
MongoDB | Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 叶小钗
T
Threatpost
Recorded Future
Recorded Future
C
CXSECURITY Database RSS Feed - CXSecurity.com
宝玉的分享
宝玉的分享
N
News and Events Feed by Topic
人人都是产品经理
人人都是产品经理
The Register - Security
The Register - Security
S
Security Archives - TechRepublic
博客园 - Franky
N
News | PayPal Newsroom
Simon Willison's Weblog
Simon Willison's Weblog
S
SegmentFault 最新的问题
W
WeLiveSecurity
A
Arctic Wolf
B
Blog

博客园 - 8user

partial关键字的含义和使用 JS仿Open()打开一个新窗口,并不是弹出新窗口 判断是否含有汉字 - 8user - 博客园 如何统计代码行执行的时间? - 8user - 博客园 时间的正则表达式验证 深入探讨IsPostBack【转】 IIS无法启动的解决方法 FCKEditor的使用及配置方法 Cool MVC: 使用正则式限定路由规则Rount - 8user C#中的值类型和引用类型 C#在线备份数据库至制定目录 - 8user - 博客园 c#实现天气预报查询 C#里面比较时间大小三种方法 - 8user - 博客园 c#中删除ArrayList中的重复数据 - 8user - 博客园 SQL Server数据库开发的二十一条规则 c#实现程序的开机启动 - 8user VS2008 快捷键大全 - 8user C#调用dos命令 反射性能测试
C#中的枚举器
8user · 2009-06-10 · via 博客园 - 8user

出处:http://www.ondotnet.com/pub/a/dotnet/2004/06/07/liberty.html

术语表 

Iterator:枚举器  

    如果你正在创建一个表现和行为都类似于集合的类,允许类的用户使用 

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

    我们将以创建一个简单化的 List Box 作为开始,它将包含一个 

8 字符串的数组和一个整型, 
这个整型用于记录数组中已经添加了多少字符串。构造函数将对数组进行初始化并使用传递进来 
的参数填充它。  
public ListBox(params string[] initialStrings) 

     strings 

= new String[8]; foreach (string s in initialStrings) 

     { 

        strings[ctr

++= s; 

     } 

    除此以外,ListBox 类还需要一个 Add方法(进行添加 

string 的操作) 和 一个返回数组中 
字符串个数的方法。  
public void Add(string theString) 

     strings[ctr] 

= theString; 

     ctr

++

public int GetNumEntries() 

return ctr; 

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

    从感觉上看,ListBox 像是一个集合,如果可以使用集合中通常使用的 

foreach 循环来获 
取 listBox中的所有字符串将会是非常便利的。如此的话,可以这样书写代码:  

ListBox lb 

= new ListBox("a""b""c""d""e""f""g""h"); foreach (string s in lb) { 

     Console.WriteLine(s); 

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

“Iterator.ListBox”不 包 含 “GetEnumerator”的 公 共 定 义 , 因 此 

foreach 语 句 不 能 作 用 于 

“Iterator.ListBox”类型的变量 

    想要使用 

foreach 语句,还必须实现 IEnumerable 接口。  

    这个接口只要求实现一个方法: GetEnumerator。这个方法必须返回一个实现了 
IEnumerator 接口的对象。除此以外,我们需要返回的这个对象不仅实现了 IEnumerator,而且 
知道如何枚举 ListBox对象。你将需要创建一个 ListBoxEmunerator(在下面描述):  

    NOTE: IEnumerable 和 IEnumerator 是不同的接口,请不要搞混了。  

public IEnumerator GetEnumerator() 

return new ListBoxEnumerator(); 

    现在,ListBox 可以使用 

foreach 循环了:  

ListBox lbt 

= new ListBox("Hello""World"); 

lbt.Add(

"Who"); 

lbt.Add(

"Is"); 

lbt.Add(

"John"); 

lbt.Add(

"Galt"); foreach (string s in lbt) 

     Console.WriteLine(

"Value: {0}", s); 

    先是实例化这个 ListBox ,并初始了两个字符串,随后又添加了四个。

foreach 循环接受 
ListBox 实例,并且迭代它,依次返回字符串。输出是:  

Hello 

World 

Who 

Is 

John 

Galt 

实现 IEnumerator  接口 

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

public class ListBox : IEnumerable 

// 嵌套的私有 ListBoxEnumerator类实现 

     
private class ListBoxEnumerator : IEnumerator 

     { 

// 代码实现 

     } 
// ListBox类的代码 

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

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

    在这个例子中,这种状态机制是通过维护一个标明当前 

string 的索引值来完成的,并且, 
你可以通过对外部类的 
string 集合进行索引来返回这个当前的 string。为了达到这个目标,你 
需要一个成员变量保存对于外部 ListBox 对象的引用,以及一个整型用于保存当前索引。  
private ListBox lbt; private int index; 

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

-1。  public void Reset() 

     index 

= -1

    每次 MoveNext 被调用的时候,外部类的数组检查时候已经到了末尾,如果是这样,方法返 
回 

false。如果集合中还有对象,index 将增加,并且方法返回 true。  public bool MoveNext() 

      index

++if (index >= lbt.strings.Length) 

      { 

return false

      }

else 

      { 

return true

      } 

     最后,如果 MoveNext 方法返回 True,

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

get { return(lbt[index]); 

      } 

     在 

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 方法。  

public IEnumerator<string> GetEnumerator() 

foreach (string s in strings) 

   { 

yield return s; 

  } 

    GetEnumerator 方法使用了新的 

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

    就这样了,你已经完成了。不需要为每个类型实现你自己的 enumerator,不需要创建嵌套 
类。你已经移除了至少 

30 行代码,并且极大地简化了你的代码。程序继续像期望的那样运行, 
但是状态管理不再是你的任务,所有的都为你做好了。更进一步,由枚举器所返回的值一定是 
string 类型,如果你想要返回其他类型,你可以修改 IEnumerable 泛型语句,IEnumerable 泛型 
语句将反射新类型。  

关于Yield 的更多内容 

    作为对上一节的一些说明,应该告诉你:实际上,你可以在 

yield 语句块中 yield 一个以上 
的值。这样,下面的语句是完全正确的 C#语句:  
public IEnumerator GetEnumerator() 

yield return "Who"yield return " is"yield return "John Galt?"

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

foreach ( string s in new foo()) 

  Console.Write(s); 

    输出结果将会是:  

Who 

is John Galt? 

    如果你现在停下来思考一下,这些也是之前的代码所做的事。它遍历了自己的 

foreach 循环, 
并且产生出它所找到的每个 string字符串。  

    本文的源代码可以在 http:

//www.tracefact.net/SourceCode/Iterators-In-CSharp.rar
下载。

posted on 2009-06-10 14:56  8user  阅读(543)  评论()    收藏  举报