在foreach語句中使用枚舉,能夠迭代數組或集合中的元素,且無須知道集合中的元素的個數。如圖顯示了調用foreach方法的客戶端和集合之間的關係。數組或集合實現帶GetEnumerator()方法的IEnumerable接口。GetEnumerator()方法返回一個實現lEnumerable接口的枚舉,接着foreach語句就能夠使用IEnumerable接口迭代集合了。數組
GetEnumerator()方法用IEnumerable接口定義,foreach語句並不真的須要在集合類中實現這個接口。有一個名爲GetEnumerator()的方法它返回實現了IEnumerator接口的對象就足夠了。ide
先定義一個Person類,這個類有自動實現的屬性Firstname和Lastname,以及從Object類重寫ToString方法和繼承泛型接口IEquatable以比較兩個對象是否相等,實現泛型接口IComparer以比較兩個對象用來排序。oop
public class Person : IEquatable<Person>,IComparable<Person> { public int Id { get; private set; } public string FirstName { get; set; } public string LastName { get; set; } public override string ToString() { return String.Format("{0}, {1} {2}", Id, FirstName, LastName); } public bool Equals(Person other) { if (other == null) return base.Equals(other); return this.FirstName == other.FirstName && this.LastName == other.LastName; } public int CompareTo(Person other) { if (other == null) throw new ArgumentNullException("other"); int result = this.LastName.CompareTo(other.LastName); if (result == 0) { result = this.FirstName.CompareTo(other.FirstName); } return result; } }
建立一個三個元素的person數組,現對數組進行排序在用foreach循環訪問數組中的元素並輸出this
Person[] persons = { new Person { FirstName = "Simen03", LastName = "Go" }, new Person { FirstName = "Simen02", LastName = "Go" }, new Person { FirstName = "Simen01", LastName = "Go" } }; Array.Sort(persons); foreach (var person in persons) Console.WriteLine(person);
分析foreach (var person in persons)Console.WriteLine(person);這段代碼IL代碼spa
// loop start (head: IL_009b) IL_008a: ldloc.2 IL_008b: ldloc.3 IL_008c: ldelem.ref IL_008d: stloc.s person IL_008f: ldloc.s person IL_0091: call void [mscorlib]System.Console::WriteLine(object) IL_0096: nop IL_0097: ldloc.3 IL_0098: ldc.i4.1 IL_0099: add IL_009a: stloc.3 IL_009b: ldloc.3 IL_009c: ldloc.2 IL_009d: ldlen IL_009e: conv.i4 IL_009f: blt.s IL_008a // end loop
C#的foreach語句不會解析爲IL代碼中的foreach語句,C#編譯器會把foreach語句轉換爲IEnumerable接口的方法和屬性,foreach語句使用IEnumerator接口的方法和屬性,迭代數組中的全部元素,爲此,IEnumerator定義了Current屬性,來返回光標所在的元素,該接口的MoveNext()方法移動到數組的下一個元素上,若是有這個元素該方法就返回true不然返回false,這個接口的泛型版本IEnumerator派生自接口IDisposable,所以定義了Dispose()方法來清理枚舉器佔用的資源,使用foreach語句會解析爲下面的代碼段code
IEnumerator enumerator = persons.GetEnumerator(); while (enumerator.MoveNext()) { var person = enumerator.Current; Console.WriteLine(person); }
爲了方便的建立枚舉器,C#添加了yield語句,yield return 語句返回集合的一個元素,並移動到下一個元素,yield break 可中止迭代。使用迭代塊,編譯器會生成一個yield類型,其中包含一個狀態機,以下代碼段所示。yield 類型實現IEnumerator和IDisposable接口的屬性和方法。在下面的例子中,能夠把yield類型看做內部類Enumerator.外部類的GetEnumerator()方法實例化並返回一個新的yield類型。在yield類型中,變量state定義了迭代的當前位置,每次調用MoveNext()時,當前位置都會改變,MoveNext()封裝了迭代代碼,並設置了current變量的值,從而使Current屬性根據位置返回一個對象。orm
static void Main(string[] args) { var helloCollection = new HelloCollection(); foreach (string s in helloCollection) { Console.WriteLine(s); } } public class HelloCollection { public IEnumerator<string> GetEnumerator() { yield return "Hello"; yield return "World"; } } public class HelloCollectionOther { public IEnumerator GetEnumertor() { return new Enumerator(0); } public class Enumerator : IEnumerator<string>, IEnumerator, IDisposable { private int state; private string current; public Enumerator(int state) { this.state = state; } public string Current => throw new NotImplementedException(); object IEnumerator.Current { get { return current; } } public void Dispose() { throw new NotImplementedException(); } public bool MoveNext() { switch (state) { case 0:current = "hello"; state = 1; return true; case 1:current = "world"; state = 2; return true; case 2: break; } return false; } public void Reset() { throw new NotImplementedException(); } } }