[C#] 實現 foreach 迴圈功能
Interduction
我們發現在使用 C# 語法開發時,漸漸的我們使用 foreach 迴圈,不會比 for loop 少;當我們在開發一個集合物件的同時,
該如何讓物件可以支持 foreach 的功能 ?
這邊討論的只是基本的用法,先將一些資源列出。
IEnumerable 介面 : 公開能逐一查看非泛型集合內容一次的列舉值。
IEnumerable 泛型介面 : 公開支援指定型別集合上簡單反覆運算的列舉值。
IEnumerator 介面 : 支援非泛型集合上的簡單反覆運算。
IEnumerator 泛型介面 : 支援泛型集合上的簡單反覆運算。
Example
在 C# 中,凡是實做了 IEnumerable 介面,就可使用 foreach 指令。先來看一下 IEnumerable 與 IEnumerator 介面原型函式。
public interface IEnumerable
{
//傳回逐一查看集合的列舉值。
IEnumerator GetEnumerator() ;
}
public interface IEnumerator
{
// 方法:將列舉值往前推至下集合中的下一個項目。
bool MoveNext();
// 方法:設定列舉值至它的初始位置,這是在集合中第一個元素之前。
void Reset();
//屬性:取得集合中目前的項目。
object Current { get; }
}
example1:(用一個類別實做兩個介面)
using System.Collections;
namespace Example1 {
//定義 Person 類別
public class Person {
private string _firstName = string.Empty;
private string _lastName = string.Empty;
public Person(string fName, string lName) {
this._firstName = fName;
this._lastName = lName;
}
public override string ToString() {
return "FirstName : " + this._firstName + ", LastName : " + this._lastName;
}
}
//----------------------------------------------------
public class DemoList : IEnumerable, IEnumerator {
private int _Index = -1;
private Person[] _PersonArr;
public DemoList() {
_PersonArr = new Person[3] {
new Person("John", "Smith"),
new Person("Jim", "Johnson"),
new Person("Sue", "Rabon"),
};
}
//實做介面 IEnumerator Reset 方法
public void Reset() {
this._Index = 0;
}
//實做介面 IEnumerator MoveNext 方法
public bool MoveNext() {
this._Index++;
if (this._Index >= this._PersonArr.Length) {
this._Index = this._PersonArr.Length - 1;
return false;
}
return true;
}
//實做介面 IEnumerator Current 屬性
public object Current {
get { return this._PersonArr[this._Index]; }
}
//實做介面 IEnumerator GetEnumerator 方法
public IEnumerator GetEnumerator() {
return (IEnumerator)this;
}
}
public class Program {
static void Main(string[] args) {
DemoList oDemoList = new DemoList();
foreach (var item in oDemoList) {
Console.WriteLine(((Person)item).ToString());
}
Console.ReadKey();
}
}
}
example2 :(用兩個類別個別實做兩個介面)
using System.Collections;
namespace Example2 {
class Program {
//定義 Person 類別
public class Person {
private string _firstName = string.Empty;
private string _lastName = string.Empty;
public Person(string fName, string lName) {
this._firstName = fName;
this._lastName = lName;
}
public override string ToString() {
return "FirstName : " + this._firstName + ", LastName : " + this._lastName;
}
}
//----------------------------------------------------
public class MyEnumerable : IEnumerable {
private Person[] _PersonArr;
public MyEnumerable() {
_PersonArr = new Person[3] {
new Person("John", "Smith"),
new Person("Jim", "Johnson"),
new Person("Sue", "Rabon"),
};
}
public IEnumerator GetEnumerator() {
return new MyEnumerator(_PersonArr);
}
}
public class MyEnumerator : IEnumerator {
private int _Index = -1;
private Person[] _PersonArr;
public MyEnumerator(Person[] PersonArr) {
this._PersonArr = PersonArr;
}
//實做介面 IEnumerator Reset 方法
public void Reset() {
this._Index = 0;
}
//實做介面 IEnumerator MoveNext 方法
public bool MoveNext() {
this._Index++;
if (this._Index >= this._PersonArr.Length) {
this._Index = this._PersonArr.Length - 1;
return false;
}
return true;
}
//實做介面 IEnumerator Current 屬性
public object Current {
get { return this._PersonArr[this._Index]; }
}
}
static void Main(string[] args) {
MyEnumerable enumerable = new MyEnumerable();
foreach (var item in enumerable) {
Console.WriteLine(((Person)item).ToString());
}
Console.ReadKey();
}
}
}
這時我們會想,我每次都要打造 IEnumerable 介面,有沒有更快速的方法?是有的 .NET 2.0 後 出現了 Iterator 的新功能,
Iterator 是一種方法、get 存取子或運算子,它可讓您支援類別或結構中的 foreach 反覆運算,而不需要實行整個 IEnumerable 介面。相反地,您只要提供 Iterator,它會只往返於類別中的資料結構。當編譯器偵測到您的 Iterator 時,它會自動產生 IEnumerable 或 IEnumerable<T> 介面的 Current、MoveNext 和 Dispose 方法。
example3 : (使用 Iterator 方法)
using System.Collections;
namespace Example3 {
//定義 Person 類別
public class Person {
private string _firstName = string.Empty;
private string _lastName = string.Empty;
public Person(string fName, string lName) {
this._firstName = fName;
this._lastName = lName;
}
public override string ToString() {
return "FirstName : " + this._firstName + ", LastName : " + this._lastName;
}
}
//----------------------------------------------------
public class DemoList {
private Person[] _PersonArr = new Person[] { };
public DemoList() {
_PersonArr = new Person[3] {
new Person("John", "Smith"),
new Person("Jim", "Johnson"),
new Person("Sue", "Rabon"),
};
}
public IEnumerable<Person> YieldReturnSample3() {
for (int N1 = 0; N1 < _PersonArr.Length; N1++) {
yield return _PersonArr[N1];
}
}
}
class Program {
static void Main(string[] args) {
DemoList oDemoList = new DemoList();
foreach (Person item in oDemoList.YieldReturnSample3()) {
Console.WriteLine(item.ToString());
}
Console.ReadKey();
}
}
}
延伸閱讀:
在安德魯大大的部落格,也有寫一篇關於這個 Iterator 的文章,其中裡面有提到,把物件巡訪的順序 (iteration) 跟依序拿到物件後要作什麼事 (process) 分開;請參考 : http://columns.chicken-house.net/post/C-yield-return-1-How-It-Work-.aspx
檔案下載: 範例一(用一個類別實做).rar、Example2.rar、Example3.rar
三小俠 小弟獻醜,歡迎指教