[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
三小俠 小弟獻醜,歡迎指教