基础【循环】-----(枚举器)------(转)
Iterator:枚舉器
如果你正在創(chuàng)建一個表現(xiàn)和行為都類似于集合的類,允許類的用戶使用foreach語句對集合中的成員進(jìn)行枚舉將會是很方便的。這在C# 2.0中比 C# 1.1更容易實(shí)現(xiàn)一些。作為演示,我們先在 C# 1.1中為一個簡單的集合添加枚舉,然后我們修改這個范例,使用新的C#2.0 枚舉構(gòu)建方法。
我們將以創(chuàng)建一個簡單化的List Box作為開始,它將包含一個8字符串的數(shù)組和一個整型,這個整型用于記錄數(shù)組中已經(jīng)添加了多少字符串。構(gòu)造函數(shù)將對數(shù)組進(jìn)行初始化并使用傳遞進(jìn)來的參數(shù)填充它。
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方法(進(jìn)行添加 string 的操作) 和 一個返回數(shù)組中字符串個數(shù)的方法。
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 CodeNOTE:實(shí)際開發(fā)中,通常使用ArrayList,而不是固定大小的數(shù)組。在這里為了程序簡單就沒有做數(shù)組下標(biāo)越界的檢測。
從感覺上看,ListBox像是一個集合,如果可以使用集合中通常使用的 foreach 循環(huán)來獲取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 }
現(xiàn)在,ListBox 可以使用 foreach 循環(huán)了:
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先是實(shí)例化這個ListBox ,并初始了兩個字符串,隨后又添加了四個。foreach循環(huán)接受ListBox實(shí)例,并且迭代它,依次返回字符串。輸出是:
1 Hello 2 World 3 Who 4 Is 5 John 6 Galt實(shí)現(xiàn) IEnumerator 接口
注意到ListBoxEnumerator不僅需要實(shí)現(xiàn)IEnumerator接口,對于ListBox類它也需要一些特別了解;特別是,它必須可以獲得ListBox的字符串?dāng)?shù)組并且遍歷其所包含的字符串。IEnumerable 類和與其相關(guān)的 IEnumerator類之間的關(guān)系有一點(diǎn)微妙。實(shí)現(xiàn)IEnumerator接口的最好辦法是在IEnumerable類里創(chuàng)建一個嵌套的IEnumerator類。
1 public class ListBox : IEnumerable 2 { 3 // 嵌套的私有ListBoxEnumerator類實(shí)現(xiàn) 4 private class ListBoxEnumerator : IEnumerator 5 { 6 // 代碼實(shí)現(xiàn)... 7 } 8 // ListBox類的代碼... 9 } View Code注意ListBoxEnumerator需要對它所嵌入的ListBox類的一個引用。你可以通過ListBoxEnumerator的構(gòu)造函數(shù)來傳遞。
為了實(shí)現(xiàn)IEnumerator接口,ListBoxEnumerator需要兩個方法:MoveNext和Reset,還有一個屬性:Current。這些方法和屬性的任務(wù)是創(chuàng)建一個狀態(tài)機(jī)制,確保你可以在任何時候得知ListBox中的哪個元素是當(dāng)前元素,并獲得那個元素。
在這個例子中,這種狀態(tài)機(jī)制是通過維護(hù)一個標(biāo)明當(dāng)前string的索引值來完成的,并且,你可以通過對外部類的string集合進(jìn)行索引來返回這個當(dāng)前的string。為了達(dá)到這個目標(biāo),你需要一個成員變量保存對于外部ListBox對象的引用,以及一個整型用于保存當(dāng)前索引。
private ListBox lbt; private int index;每次Reset方法被調(diào)用的時候,index被置為 -1。
1 public void Reset() 2 { 3 index = -1; 4 }每次MoveNext被調(diào)用的時候,外部類的數(shù)組檢查時候已經(jīng)到了末尾,如果是這樣,方法返回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循環(huán)將調(diào)用Current屬性。ListBoxEnumerator的Current屬性的實(shí)現(xiàn)是索引外部類(ListBox)中的集合,并且返回找到的對象(這個例子中,是一個字符串)。注意,返回一個Object是因?yàn)镮Enumerator接口中Current屬性的簽名如此。
1 public object Current 2 { 3 get { 4 return(lbt[index]); 5 } 6 } View Code在1.1中,所有想要通過foreach循環(huán)來迭代的類都需要實(shí)現(xiàn)IEnumerable接口,于是,必須創(chuàng)建一個實(shí)現(xiàn)了IEnumerator的類。最糟的是,enumerator返回的值并不是類型安全的。記得Current屬性返回一個Object對象;它僅僅簡單的假設(shè)你所返回的值與foreach循環(huán)所期望的相符合。
C# 2.0 的解救辦法
使用C# 2.0 這些問題如同五月末的雪般融化了。在這個例子的2.0版本中,我重寫上面的列表,使用C# 2.0的兩個新特性:泛型 和 枚舉器。
我以重新定義實(shí)現(xiàn)IEumerable<string>的ListBox作為開始:
public class ListBox : IEnumerable<string>這樣做確定這個類可以在foreach循環(huán)中使用,同時確保迭代的值是string類型。
現(xiàn)在,從上個例子中挪去整個嵌套類,并且用下面的代碼替換 GetEnumerator方法。
1 public IEnumerator<string> GetEnumerator() 2 { 3 foreach (string s in strings) 4 { 5 yield return s; 6 } 7 } View CodeGetEnumerator方法使用了新的 yield 語句。yield語句返回一個表達(dá)式。yield語句僅在迭代塊中出現(xiàn),并且返回foreach語句所期望的值。那也就是,對GetEnumerator的每次調(diào)用都將會產(chǎn)生集合中的下一個字符串;所有的狀態(tài)管理已經(jīng)都為你做好了!
就這樣了,你已經(jīng)完成了。不需要為每個類型實(shí)現(xiàn)你自己的enumerator,不需要創(chuàng)建嵌套類。你已經(jīng)移除了至少30行代碼,并且極大地簡化了你的代碼。程序繼續(xù)像期望的那樣運(yùn)行,但是狀態(tài)管理不再是你的任務(wù),所有的都為你做好了。更進(jìn)一步,由枚舉器所返回的值一定是string類型,如果你想要返回其他類型,你可以修改IEnumerable泛型語句,IEnumerable泛型語句將反射新類型。
關(guān)于Yield的更多內(nèi)容
作為對上一節(jié)的一些說明,應(yīng)該告訴你:實(shí)際上,你可以在yield語句塊中yield一個以上的值。這樣,下面的語句是完全正確的C#語句:
1 public IEnumerator GetEnumerator() 2 { 3 yield return "Who"; 4 yield return " is"; 5 yield return "John Galt?"; 6 } View Code假設(shè)上面的代碼位于一個名為foo的類中,你可以這樣寫:
1 foreach ( string s in new foo()) 2 { 3 Console.Write(s); 4 }輸出結(jié)果將會是:
Who is John Galt?如果你現(xiàn)在停下來思考一下,這些也是之前的代碼所做的事。它遍歷了自己的foreach循環(huán),并且產(chǎn)生出它所找到的每個string字符串。
?
轉(zhuǎn)載于:https://www.cnblogs.com/shao-shao/articles/3447841.html
總結(jié)
以上是生活随笔為你收集整理的基础【循环】-----(枚举器)------(转)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux字符编码转换 UTF8转GB3
- 下一篇: 电脑网易云音乐,我放弃了QQ音乐和网易云