迭代器

迭代器(C# 編程指南)

迭代器是 C# 2.0 中的新功能。迭代器是方法、get 訪問器或運算符,它使您可以在結構中支持 foreach 迭代,而沒必要實現整個 IEnumerable 接口。您只需提供一個迭代器,便可遍歷類中的數據結構。當編譯器檢測到迭代器時,它將自動生成 IEnumerableIEnumerable<T> 接口的 CurrentMoveNextDispose 方法。編程

迭代器概述

  • 迭代器是能夠返回相同類型的值的有序序列的一段代碼。數組

  • 迭代器可用做方法、運算符或 get 訪問器的代碼體。數據結構

  • 迭代器代碼使用 yield return 語句依次返回每一個元素。yield break 將終止迭代。有關更多信息,請參見 yieldless

  • 能夠在類中實現多個迭代器。每一個迭代器都必須像任何類成員同樣有惟一的名稱,而且能夠在 foreach 語句中被客戶端代碼調用,以下所示:foreach(int x in SampleClass.Iterator2){}ui

  • 迭代器的返回類型必須爲 IEnumerableIEnumeratorIEnumerable<T> 或 IEnumerator<T>。this

yield 關鍵字用於指定返回的值。到達 yield return 語句時,會保存當前位置。下次調用迭代器時將今後位置從新開始執行。spa

迭代器對集合類特別有用,它提供一種簡單的方法來迭代不經常使用的數據結構(如二進制樹)。3d

示例

在本示例中,DaysOfTheWeek 類是將一週中的各天做爲字符串進行存儲的簡單集合類。foreach 循環每迭代一次,都返回集合中的下一個字符串。code

public class DaysOfTheWeek : System.Collections.IEnumerable
{
    string[] m_Days = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };

    public System.Collections.IEnumerator GetEnumerator()
    {
        for (int i = 0; i < m_Days.Length; i++)
        {
            yield return m_Days[i];
        }
    }
}

class TestDaysOfTheWeek
{
    static void Main()
    {
        // Create an instance of the collection class
        DaysOfTheWeek week = new DaysOfTheWeek();

        // Iterate with foreach
        foreach (string day in week)
        {
            System.Console.Write(day + " ");
        }
    }
}

輸出

Sun Mon Tue Wed Thr Fri Sat

 

使用迭代器

建立迭代器最經常使用的方法是對 IEnumerable 接口實現 GetEnumerator 方法,例如:繼承

public System.Collections.IEnumerator GetEnumerator()
{
    for (int i = 0; i < max; i++)
    {
        yield return i;
    }
}

GetEnumerator 方法的存在使得類型成爲可枚舉的類型,並容許使用 foreach 語句。若是上面的方法是 ListClass 的類定義的一部分,則能夠對該類使用 foreach,以下所示:

static void Main()
{
    ListClass listClass1 = new ListClass();

    foreach (int i in listClass1)
    {
        System.Console.WriteLine(i);
    }
}

foreach 語句調用 ListClass.GetEnumerator() 並使用返回的枚舉數來循環訪問值。有關如何建立返回 IEnumerator 接口的泛型迭代器的示例,請參見如何:爲泛型列表建立迭代器塊(C# 編程指南)

還可使用命名的迭代器以支持經過不一樣的方式循環訪問同一數據集合。例如,您能夠提供一個按升序返回元素的迭代器,而提供按降序返回元素的另外一個迭代器。迭代器還能夠帶有參數,以便容許客戶端控制所有或部分迭代行爲。下面的迭代器使用命名的迭代器 SampleIterator 實現 IEnumerable 接口:

// Implementing the enumerable pattern
public System.Collections.IEnumerable SampleIterator(int start, int end)
{
    for (int i = start; i <= end; i++)
    {
        yield return i;
    }
}

命名的迭代器的調用方法以下:

ListClass test = new ListClass();
foreach (int n in test.SampleIterator(1, 10))
{
    System.Console.WriteLine(n);
}

能夠在同一個迭代器中使用多個 yield 語句,以下面的示例所示:

public System.Collections.IEnumerator GetEnumerator()
{
    yield return "With an iterator, ";
    yield return "more than one ";
    yield return "value can be returned";
    yield return ".";
}

而後可使用下面的 foreach 語句輸出結果:

foreach (string element in new TestClass())
{
    System.Console.Write(element);
}

此示例顯示如下文本:

With an iterator, more than one value can be returned.

foreach 循環的每次後續迭代(或對 IEnumerator.MoveNext 的直接調用)中,下一個迭代器代碼體將從前一個 yield 語句以後開始,並繼續下一個語句直至到達迭代器體的結尾或遇到 yield break 語句。

 

 

如何:爲整數列表建立迭代器塊

此示例使用一個整數數組來構建列表 SampleCollectionfor 循環會循環訪問該集合,並生成每一個項的值。而後使用 foreach 循環來顯示該集合的項。

示例

// Declare the collection:
public class SampleCollection
{
    public int[] items;

    public SampleCollection()
    {
        items = new int[5] { 5, 4, 7, 9, 3 };
    }

    public System.Collections.IEnumerable BuildCollection()
    {
        for (int i = 0; i < items.Length; i++)
        {
            yield return items[i];
        }
    }
}

class MainClass
{
    static void Main()
    {
        SampleCollection col = new SampleCollection();

        // Display the collection items:
        System.Console.WriteLine("Values in the collection are:");
        foreach (int i in col.BuildCollection())
        {
            System.Console.Write(i + " ");
        }
    }
}

輸出

Values in the collection are:
5 4 7 9 3

 

如何:爲泛型列表建立迭代器塊

在本示例中,泛型Stack<T>實現泛型接口 IEnumerator<T>。聲明瞭一個類型 T 的數組,並使用 Push 方法給數組賦值。在 GetEnumerator 方法中,使用 yield return 語句返回數組的值。

還實現非泛型 GetEnumerator,由於 IEnumerable<T> 繼承自 IEnumerable。此示例顯示了典型的實現,在該實現中,非泛型方法直接將調用轉給泛型方法。

示例

using System.Collections;
using System.Collections.Generic;

namespace GenericIteratorExample
{
    public class Stack<T> : IEnumerable<T>
    {
        private T[] values = new T[100];
        private int top = 0;

        public void Push(T t) { values[top++] = t; }
        public T Pop() { return values[--top]; }

        // These make Stack<T> implement IEnumerable<T> allowing
        // a stack to be used in a foreach statement.
        public IEnumerator<T> GetEnumerator()
        {
            for (int i = top; --i >= 0; )
            {
                yield return values[i];
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        // Iterate from top to bottom.
        public IEnumerable<T> TopToBottom
        {
            get
            {
                // Since we implement IEnumerable<T>
                // and the default iteration is top to bottom,
                // just return the object.
                return this;
            }
        }

        // Iterate from bottom to top.
        public IEnumerable<T> BottomToTop
        {
            get
            {
                for (int i = 0; i < top; i++)
                {
                    yield return values[i];
                }
            }
        }

        //A parameterized iterator that return n items from the top
        public IEnumerable<T> TopN(int n)
        {
            // in this example we return less than N if necessary 
            int j = n >= top ? 0 : top - n;

            for (int i = top; --i >= j; )
            {
                yield return values[i];
            }
        }
    }

    //This code uses a stack and the TopToBottom and BottomToTop properties 
    //to enumerate the elements of the stack.
    class Test
    {
        static void Main()
        {
            Stack<int> s = new Stack<int>();
            for (int i = 0; i < 10; i++)
            {
                s.Push(i);
            }

            // Prints: 9 8 7 6 5 4 3 2 1 0
            // Foreach legal since s implements IEnumerable<int>
            foreach (int n in s)
            {
                System.Console.Write("{0} ", n);
            }
            System.Console.WriteLine();

            // Prints: 9 8 7 6 5 4 3 2 1 0
            // Foreach legal since s.TopToBottom returns IEnumerable<int>
            foreach (int n in s.TopToBottom)
            {
                System.Console.Write("{0} ", n);
            }
            System.Console.WriteLine();

            // Prints: 0 1 2 3 4 5 6 7 8 9
            // Foreach legal since s.BottomToTop returns IEnumerable<int>
            foreach (int n in s.BottomToTop)
            {
                System.Console.Write("{0} ", n);
            }
            System.Console.WriteLine();

            // Prints: 9 8 7 6 5 4 3
            // Foreach legal since s.TopN returns IEnumerable<int>
            foreach (int n in s.TopN(7))
            {
                System.Console.Write("{0} ", n);
            }
            System.Console.WriteLine();
        }
    }
}

輸出

9 8 7 6 5 4 3 2 1 0 
9 8 7 6 5 4 3 2 1 0 
0 1 2 3 4 5 6 7 8 9 
9 8 7 6 5 4 3 
相關文章
相關標籤/搜索