[C#] 實現 foreach 迴圈功能

  • 25515
  • 0

[C#] 實現 foreach 迴圈功能

Interduction

我們發現在使用 C# 語法開發時,漸漸的我們使用 foreach 迴圈,不會比 for loop 少;當我們在開發一個集合物件的同時,

該如何讓物件可以支持 foreach 的功能 ?

這邊討論的只是基本的用法,先將一些資源列出。

IEnumerable 介面 : 公開能逐一查看非泛型集合內容一次的列舉值。

IEnumerable 泛型介面 : 公開支援指定型別集合上簡單反覆運算的列舉值。

IEnumerator 介面 : 支援非泛型集合上的簡單反覆運算。

IEnumerator 泛型介面 : 支援泛型集合上的簡單反覆運算。

IEnumerable 成員

IEnumerator 成員

Iterator (C# 程式設計手冊)

使用 Iterator (C# 程式設計手冊)

yield (C# 參考)

 

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 時,它會自動產生 IEnumerableIEnumerable<T> 介面的 CurrentMoveNextDispose 方法。

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

 

檔案下載: 範例一(用一個類別實做).rarExample2.rarExample3.rar

三小俠  小弟獻醜,歡迎指教