迭代器(IEnumerator、IEnumerable、yield)

其他相關文章:
https://www.cnblogs.com/zhaopei/p/5769782.html
https://www.cnblogs.com/murongxiaopifu/p/4437432.html
https://www.cnblogs.com/yangecnu/archive/2012/03/17/2402432.html

C#從2.0版本開始提供了更簡單的創建枚舉器和可枚舉類型的方式,實際上編譯器將爲我們創建它們,這種結構叫迭代器,我們可以把手動編碼的可枚舉類型和枚舉器替換爲由迭代器生成的可枚舉類型和枚舉器。

迭代器塊

迭代器塊是有一個或多個yield語句的代碼塊,迭代器塊與其他代碼塊不同,其他塊包含的語句被當作是命令式的,也就是說先執行代碼塊的第一個語句,然後執行後面的語句,最後控制離開塊,而迭代器塊不需要在同一時間執行一串命令,而是描述了希望編譯器爲我們創建的枚舉器的行爲,迭代器塊中的代碼描述瞭如何枚舉元素

迭代器塊有倆個特殊語句:(1)yield return語句指定了序列中返回的下一項 (2)yield break語句指定在序列中沒有其他項。

編譯器得到有關如何枚舉項的描述後,使用它來構建包含所有需要的方法和屬性實現的枚舉器類,結果類被嵌套包含在迭代器聲明的類中如第二張圖中這個結果類(嵌套類)實現了IEnumerator<string>,並且被迭代器聲明類MyClass所包含

使用迭代器來創建枚舉器

BlackAndWhite方法時一個迭代器塊,可以爲MyClass類產生返回枚舉器的方法。

    class MyClass
    {
        public IEnumerator<string> GetEnumerator() { return BlackAndWhite(); }
        public IEnumerator<string> BlackAndWhite() //迭代器
        {
            yield return "black";
            yield return "gray";
            yield return "white";
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            MyClass mc = new MyClass();
            foreach (var v in mc)
                Console.WriteLine(v);
        }
    }

使用迭代器來創建可枚舉類型

在本例中,BlackAndWrite迭代器方法返回IEnumerble<string>而不是IEnumerator<string>,因此,MyClass首先調用BlackAndWhite方法獲取它的可枚舉類型對象,然後調用對象的GetEnum方法來獲取它的結果,從而實現GetEnumerator方法在Main中。我們可以使用類的實例,也可以調用BlackAndWhite方法,因爲它返回的是可枚舉類型

   class MyClass
    {
        public IEnumerator<string> GetEnumerator()
        {
            IEnumerable<string> myEnumerable = BlackAndWhite(); //獲取可枚舉類型
            return myEnumerable.GetEnumerator();  //獲取枚舉器
        }
        public IEnumerable<string> BlackAndWhite() //返回可枚舉類型
        {
            yield return "black";
            yield return "gray";
            yield return "white";
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            MyClass mc = new MyClass();
            foreach (var v in mc)   //使用類對象
                Console.WriteLine(v);

            foreach (var v2 in mc.BlackAndWhite())  //使用類枚舉器方法
                Console.WriteLine(v2);
        }
    }

倆種迭代器模式

1.當我們實現返回枚舉器的迭代器時,必須通過實現GetEnumerator來讓類可枚舉,它返回由迭代器返回的枚舉器如圖左部分
2.如果我們在類中實現迭代器返回可枚舉類型,可以讓類實現GetEnumerator來讓類本身可被枚舉,或不實現GetEnumerator,讓類不可枚舉。
   (1)如果實現GetEnumerator,讓他調用迭代器方法以獲取自動生成的實現IEnumerator的類實例,然後,從IEnumerator對象返回由GetEnumerator創建的枚舉器,如圖右邊所示
   (2)如果通過不實現GetEnumerator使類本身不可枚舉,仍然可以使用由迭代器返回的可枚舉類只需要直接調用迭代器方法,如右圖第二個foreach

將迭代器作爲屬性

    class Spectrum
    {
        bool _listFromUVtoIR;

        string[] colors = { "violet", "blue", "cyan", "green" };

        public Spectrum(bool listFromUVtoIR)
        {
            _listFromUVtoIR = listFromUVtoIR;
        }

        public IEnumerator<string> GetEnumerator()
        {
            return _listFromUVtoIR ? UVtoIR : IRtoUV;
        }

        //正序
        public IEnumerator<string> UVtoIR
        {
            get
            {
                for (int i = 0; i < colors.Length; i++)
                    yield return colors[i];
            }
        }

        //逆序
        public IEnumerator<string> IRtoUV
        {
            get
            {
                for (int i = colors.Length - 1; i >= 0; i--)
                    yield return colors[i];
            }
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            Spectrum startUV = new Spectrum(true);
            Spectrum startIR = new Spectrum(false);

            foreach (string color in startUV)
                Console.Write("{0}", color);
            Console.WriteLine();

            foreach (string color in startIR)
                Console.Write("{0}", color);
            Console.WriteLine();
        }
    }

迭代器實質

在編譯器生成枚舉器中,Reset方法沒有實現,而它是接口需要的方法,因此調用時總是拋出System.NotSupportedException異常,在圖18-9中Reset方法顯示爲灰色,在後臺,由編譯器生成的枚舉器類是包含4個狀態的狀態機。

1.Before 首次調用MoveNext的初始狀態
2.Running 調用MoveNext後進入這個狀態,在這個狀態中,枚舉器檢測並設置下一項的位置,在遇到yield return、yield break或在迭代器體結束時,退出狀態
3.Suspended 狀態機等待下次調用MoveNext的狀態
4.After 沒有更多項可以枚舉

如果狀態機在Before或Suspended狀態時調用了MoveNext方法,就轉到了Running狀態,在Running狀態中,它檢測集合的下一項並設置位置,如果有更多項,狀態機會進入Suspended狀態,如果沒有更多項,它轉入並保持在After狀態。

 

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