下面我們先看IEnumerable和IEnumerator兩個接口的語法定義。其實IEnumerable接口是非常的簡單,只包含一個抽象的方法GetEnumerator(),它返回一個可用于循環(huán)訪問集合的IEnumerator對象。IEnumerator對象有什么呢?它是一個真正的集合訪問器,沒有它,就不能使用foreach語句遍歷集合或數(shù)組,因為只有IEnumerator對象才能訪問集合中的項,假如連集合中的項都訪問不了,那么進行集合的循環(huán)遍歷是不可能的事情了。那么讓我們看看IEnumerator接口有定義了什么東西??聪聢D我們知道IEnumerator接口定義了一個Current屬性,MoveNext和Reset兩個方法,這是多么的簡約。既然IEnumerator對象時一個訪問器,那至少應(yīng)該有一個Current屬性,來獲取當(dāng)前集合中的項吧。
MoveNext方法只是將游標(biāo)的內(nèi)部位置向前移動(就是移到一下個元素而已),要想進行循環(huán)遍歷,不向前移動一下怎么行呢?


詳細講解:
說到IEnumerable總是會和IEnumerator、foreach聯(lián)系在一起。
C# 支持關(guān)鍵字foreach,允許我們遍歷任何數(shù)組類型的內(nèi)容:
//遍歷數(shù)組的項
int[] myArrayOfInts = {10,20,30,40};
foreach(int i in my myArrayOfInts)
{
Console.WirteLine(i);
}
雖然看上去只有數(shù)組才可以使用這個結(jié)構(gòu),其實任何支持GetEnumerator()方法的類型都可以通過foreach結(jié)構(gòu)進行運算。
- public class Garage
- {
- Car[] carArray = new Car[4];
-
-
- public Garage()
- {
-
- carArray[0] = new Car("Rusty", 30);
- carArray[1] = new Car("Clunker", 50);
- carArray[2] = new Car("Zippy", 30);
- carArray[3] = new Car("Fred", 45);
- }
- }
理想情況下,與數(shù)據(jù)值數(shù)組一樣,使用foreach構(gòu)造迭代Garage對象中的每一個子項比較方便:
-
- lass Program
- {
- static void Main(string[] args)
- {
- Console.WriteLine("*********Fun with IEnumberable/IEnumerator************\n");
- Garage carLot = new Garage();
-
-
- foreach (Car c in carLot)
- {
- Console.WriteLine("{0} is going {1} MPH", c.CarName, c.CurrentSpeed);
- }
-
- Console.ReadLine();
- }
- }
讓人沮喪的是,編譯器通知我們Garage類沒有實現(xiàn)名為GetEnumerator()的方法(顯然用foreach遍歷Garage對象是不可能的事情,因為Garage類沒有實現(xiàn)GetEnumerator()方法,Garage對象就不可能返回一個IEnumerator對象,沒有IEnumerator對象,就不可能調(diào)用方法MoveNext(),調(diào)用不了MoveNext,就不可能循環(huán)的了)。這個方法是有隱藏在System.collections命名空間中的IEnumerable接口定義的。(特別注意,其實我們循環(huán)遍歷的都是對象而不是類,只是這個對象是一個集合對象)
支持這種行為的類或結(jié)構(gòu)實際上是宣告它們向調(diào)用者公開所包含的子項:
//這個接口告知調(diào)方對象的子項可以枚舉
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
可以看到,GetEnumerator方法返回對另一個接口System.Collections.IEnumerator的引用。這個接口提供了基礎(chǔ)設(shè)施,調(diào)用方可以用來移動IEnumerable兼容容器包含的內(nèi)部對象。
//這個接口允許調(diào)用方獲取一個容器的子項
public interface IEnumerator
{
bool MoveNext(); //將游標(biāo)的內(nèi)部位置向前移動
object Current{get;} //獲取當(dāng)前的項(只讀屬性)
void Reset(); //將游標(biāo)重置到第一個成員前面
}
所以,要想Garage類也可以使用foreach遍歷其中的項,那我們就要修改Garage類型使之支持這些接口,可以手工實現(xiàn)每一個方法,不過這得花費不少功夫。雖然自己開發(fā)GetEnumerator()、MoveNext()、Current和Reset()也沒有問題,但有一個更簡單的辦法。因為System.Array類型和其他許多類型(如List)已經(jīng)實現(xiàn)了IEnumerable和IEnumerator接口,你可以簡單委托請求到System.Array,如下所示:
- namespace MyCarIEnumerator
- {
- public class Garage:IEnumerable
- {
- Car[] carArray = new Car[4];
-
-
- public Garage()
- {
- carArray[0] = new Car("Rusty", 30);
- carArray[1] = new Car("Clunker", 50);
- carArray[2] = new Car("Zippy", 30);
- carArray[3] = new Car("Fred", 45);
- }
- public IEnumerator GetEnumerator()
- {
- return this.carArray.GetEnumerator();
- }
- }
- }
-
-
- namespace MyCarIEnumerator
- {
- class Program
- {
- static void Main(string[] args)
- {
- Console.WriteLine("*********Fun with IEnumberable/IEnumerator************\n");
- Garage carLot = new Garage();
-
-
- foreach (Car c in carLot)
- {
- Console.WriteLine("{0} is going {1} MPH", c.CarName, c.CurrentSpeed);
- }
-
- Console.WriteLine("GetEnumerator被定義為公開的,對象用戶可以與IEnumerator類型交互,下面的結(jié)果與上面是一致的");
-
- IEnumerator i = carLot.GetEnumerator();
- while (i.MoveNext())
- {
- Car myCar = (Car)i.Current;
- Console.WriteLine("{0} is going {1} MPH", myCar.CarName, myCar.CurrentSpeed);
- }
- Console.ReadLine();
- }
- }
- }
下面我們來看看手工實現(xiàn)IEnumberable接口和IEnumerator接口中的方法:
- namespace ForeachTestCase
- {
-
- class ForeachTest:IEnumerable {
- private string[] elements;
- private int ctr = 0;
-
-
-
-
-
- ForeachTest(params string[] initialStrings)
- {
-
- elements = new String[8];
-
- foreach (string s in initialStrings)
- {
- elements[ctr++] = s;
- }
- }
-
-
-
-
-
-
- ForeachTest(string initialStrings, char[] delimiters)
- {
- elements = initialStrings.Split(delimiters);
- }
-
-
- public IEnumerator GetEnumerator()
- {
- return new ForeachTestEnumerator(this);
- }
-
- private class ForeachTestEnumerator : IEnumerator
- {
- private int position = -1;
- private ForeachTest t;
- public ForeachTestEnumerator(ForeachTest t)
- {
- this.t = t;
- }
-
- #region 實現(xiàn)接口
-
- public object Current
- {
- get
- {
- return t.elements[position];
- }
- }
-
- public bool MoveNext()
- {
- if (position < t.elements.Length - 1)
- {
- position++;
- return true;
- }
- else
- {
- return false;
- }
- }
-
- public void Reset()
- {
- position = -1;
- }
-
- #endregion
- }
- static void Main(string[] args)
- {
-
- ForeachTest f = new ForeachTest("This", "is", "a", "sample", "sentence.");
- foreach (string item in f)
- {
- System.Console.WriteLine(item);
- }
- Console.ReadKey();
- }
- }
- }
IEnumerable<T>接口
實現(xiàn)了IEnmerable<T>接口的集合,是強類型的。它為子對象的迭代提供類型更加安全的方式。
- public class ListBoxTest:IEnumerable<String>
- {
- private string[] strings;
- private int ctr = 0;
-
- #region IEnumerable<string> 成員
-
- public IEnumerator<string> GetEnumerator()
- {
- foreach (string s in strings)
- {
- yield return s;
- }
- }
-
- #endregion
-
- #region IEnumerable 成員
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
-
- #endregion
-
-
- public ListBoxTest(params string[] initialStrings)
- {
-
- strings = new String[8];
-
- foreach (string s in initialStrings)
- {
- strings[ctr++] = s;
- }
- }
-
-
- public void Add(string theString)
- {
- strings[ctr] = theString;
- ctr++;
- }
-
-
- public string this[int index]
- {
- get {
- if (index < 0 || index >= strings.Length)
- {
-
- }
- return strings[index];
- }
- set {
- strings[index] = value;
- }
- }
-
-
- public int GetNumEntries()
- {
- return ctr;
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
-
- ListBoxTest lbt = new ListBoxTest("Hello", "World");
-
-
- lbt.Add("Who");
- lbt.Add("Is");
- lbt.Add("Douglas");
- lbt.Add("Adams");
-
-
- string subst = "Universe";
- lbt[1] = subst;
-
-
- foreach (string s in lbt)
- {
- Console.WriteLine("Value:{0}", s);
- }
- Console.ReadKey();
- }
- }
綜上所述,一個類型是否支持foreach遍歷,必須滿足下面條件:
方案1:讓這個類實現(xiàn)IEnumerable接口
方案2:這個類有一個public的GetEnumerator的實例方法,并且返回類型中有public 的bool MoveNext()實例方法和public的Current實例屬性。
IEnumerable接口和IEnumerator接口是.NET中非常重要的接口,二者有何區(qū)別?
1. 簡單來說IEnumerable是一個聲明式的接口,聲明實現(xiàn)該接口的類就是“可迭代的enumerable”,但并沒用說明如何實現(xiàn)迭代器(iterator).其代碼實現(xiàn)為:
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
2. 而IEnumerator接口是實現(xiàn)式接口,它聲明實現(xiàn)該接口的類就可以作為一個迭代器iterator.其代碼實現(xiàn)為:
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
3.一個collection要支持Foreach進行遍歷,就必須實現(xiàn)IEnumerable,并一某種方式返回迭代器對象:IEnumerator.
那么又如何實現(xiàn)這兩個接口呢? 其代碼如下:
假設(shè)有一個Person類,其有兩個屬性FirstName和LastName
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = lastName;
}
}
另外通過People類來實現(xiàn)IEnumerable和IEnumerator接口.
//實現(xiàn)IEnumerable接口
public class People :IEnumerable
{
public Person [] pers;
public People(Person [] ps)
{
this.pers = ps;
}
public IEnumerator GetEnumerator()
{
//foreach(Person p in pers)
// {
// yield return p;
// }
return new People1(pers);
}
}
//實現(xiàn)IEnumerator接口
public class People1 : IEnumerator
{
public Person[] pers;
public People1(Person[] per)
{
this.pers = per;
}
int position = -1;
public bool MoveNext()
{
position++;
return position < pers.Length;
}
public void Reset()
{
position=-1;
}
public object Current
{
get
{
try
{
return pers[position];
}
catch(IndexOutOfRangeException ex)
{
throw new InvalidOperationException();
}
}
}
}