C#2.0迭代器

C#的foreach語句常用來迭代可枚舉的集合的元素。爲了實現可枚舉,一個集合必須有一個無參的GetEnumerator方法,這個方法返回一個枚舉器。通常,枚舉器比較難實現,但是,用迭代器來簡化(枚舉器)的任務十分有意義。
 

一個迭代器是產生一個有序的值序列的一個語句塊 。一個迭代器由出現一個或者多個yield語句而區別於一般的語句塊:
 

 

只要函數成員的返回類型是枚舉器接口或者enumerable接口中的一個,那麼一個迭代器就可以被用作函數成員體。
 

重要的是明白一個迭代器不是一種成員,而是一種實現函數成員的手段。一個通過迭代器實現的成員能被其他成員覆蓋或者重載,而那些成員可能是迭代器實現的,也可能不是迭代器實現的。
 

以下的Stack<T>類使用迭代器實現它的GetEnumerator方法。迭代器自頂向下枚舉棧裏的元素。

using System.Collections.Generic;

class Stack<T> : IEnumerable<T>
    {
        T[] items ;
        int count ;
        public void Push(T data)
        {
           
            if (items == null)
            {

                items = new T[1];

            }
            else if (items.Length == count)
            {

                T[] newItems = new T[count * 2];

                Array.Copy(items, 0, newItems, 0, count);

                items = newItems;

            }

            items[count++] = data;
            
           

        }
        public T Pop()
        {
            T result = items[--count];

            items[count] = default(T);

            return result;

        }

 


        public IEnumerator<T> GetEnumerator()
        {
            for (int i = count - 1; i >= 0; i--)
            {
                yield return items[i];

            }
            //throw new Exception("The method or operation is not implemented.");
        }

 

        #region IEnumerable 成員

        IEnumerator IEnumerable.GetEnumerator()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion
    }
GetEnumerator方法的出現使得Stack<T>成爲一個可枚舉類型,這允許Stack<T>的實例使用foreach語句。下面的例子將值0-9壓入一個整數堆棧,然後用foreach循環按照從頂端到低端的順序顯示每一個值。

 class diedaiqi
    {
        static IEnumerable<int> FromTo(int from, int to)
        {

            while (from <= to) yield return from++;

        }

        static void Main()
        {
            Stack<int> stack = new Stack<int>();

            for (int i = 0; i < 10; i++)
            {
                stack.Push(i);
            }

            foreach (int i in stack)
            {
                Console.Write("{0} ", i);
            }

            Console.WriteLine();

        }
    }

語句隱式地調用了集合的無參的GetEnumerator方法來得到一個枚舉器。一個集合類中只能定義一個這樣的無參的GetEnumerator方法,不過通常可以通過很多途徑來實現枚舉,包括使用參數來控制枚舉。在這些情況下,一個集合可以使用迭代器來實現能夠返回可枚舉接口的屬性和方法。例如,Stack<T>可以引入兩個新屬性;IEnumerable<T>類型的TopToBottom和BottomToTop
         public IEnumerable<T> TopToBottom
        {
            get
            {
                return this;
            }
        }

        public IEnumerable<T> BottomToTop
        {
            get
            {
                for (int i = 0; i < count; i++)
                {
                    yield return items[i];
                }
            }
        }
TopToBottom 屬性的get訪問器只是返回this,因爲棧自身是一個enumerable。BottomToTop屬性使用C#迭代器返回一個enumerable。以下的例子展示怎麼使用屬性以兩種不同的順序枚舉元素:
            static void Main()
           {

               Stack<int> stack = new Stack<int>();
               for (int i = 0; i < 10; i++) stack.Push(i);
               foreach (int i in stack.TopToBottom) Console.Write("{0} ", i);
               Console.WriteLine();
               foreach (int i in stack.BottomToTop) Console.Write("{0} ", i);
               Console.WriteLine();
            }
當然,這些屬性也能使用在foreach語句之外。以下例子傳遞調用屬性的結果(作爲參數)給(獨立的)Print方法。這個例子還展示了一個迭代器用作爲FromToBy方法體,接受參數。
class diedaiqi
    {

 

        static void Print(IEnumerable<int> collection)
        {
            foreach (int i in collection)
            {
                Console.Write("{0} ", i);
            }

            Console.WriteLine();
        }

        static IEnumerable<int> FromToBy(int from, int to, int by)
        {
            for (int i = from; i <= to; i+= by)
            {
                yield return i;
            }
        }

        static void Main()
        {

 

            Stack<int> stack = new Stack<int>();
            for (int i = 0; i < 10; i++) stack.Push(i);
            Print(stack.TopToBottom);
            Print(stack.BottomToTop);
            Print(FromToBy(10,20,2));

 

        }
    }

泛型和非泛型enumerable接口包含一個單獨的成員,即一個不帶任何參數GetEnumerator方法,這個方法返回一個枚舉器接口。一個enumerable相當於一個枚舉器工廠。當每次它們的GetEnumerator方法被調用,要適當地實現enumerables來獲得獨立的枚舉器。假設enumerable的內狀態在兩次調用GetEnumerator沒有變化,兩個返回的枚舉器應該產生以相同順序排列的相同值集合。甚至枚舉器的生存期以下面的代碼實例交替, 這點也必須保持:
 

using System;
using System.Collections.Generic;
class Test
{
    
static IEnumerable<int> FromTo(int from, int to) {
        
while (from <= to) yield return from++;
    }

    static void Main() {
        IEnumerable
<int> e = FromTo(110);
        
foreach (int x in e) {
            
foreach (int y in e) {
                Console.Write(
"{0,3} ", x * y);
            }

            Console.WriteLine();
        }

    }
}

 

以上代碼輸出一個從整數1到10的簡單的乘法表。注意FromTo方法只被調用一次來獲取enumerable。然而,e.GetEnumerator()被調用多次(被foreach語句)來獲取多個等價枚舉器。

這些枚舉器都封裝迭代器指定的代碼在FromTo的聲明裏。注意迭代器代碼修改了from參數。然而,枚舉器獨立運作,因爲每個枚舉器擁有了它自己的from和to參數的副本。在實現enumerables和枚舉器時,枚舉器之間的過渡狀態的共享是應該被避免的一些細微的缺陷中的一個。C#迭代器被設計來幫助避免這些問題,(並且)以一種簡單直觀的方式實現健壯的枚舉和枚舉器。

· Yield return 語句產生迭代的下一個值。

· Yield break 語句指出這個迭代結束。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章