C# 迭代器與yield關鍵字

迭代器模式是設計模式的一種,由於其運用的廣泛性,不少語言都有內嵌的原生支持設計模式

在.NET中,迭代器模式是經過IEnumeratorIEnumerable兩個接口(有非泛型和泛型2種版本)來封裝的api

迭代器模式的一個重要方面是:不是一次返回全部數據,而是每次調用只返回一個元素函數

 

Array、IEnumerable和IEnumerator之間關係以下: this

foreach遍歷spa

① Array、集合容器類派生於IEnumerable接口類(該類中含有一個IEnumerator GetEnumerator()接口函數),Array、集合容器類實現了該函數,這使得foreach可對其進行遍歷設計

    注:不須要從IEnumerable派生,只要類實現了IEnumerator GetEnumerator()函數,就能夠使用foreach遍歷該類中的元素code

② IEnumerator定義了Current屬性來返回遊標所在的元素,MoveNext方法移動到下一個元素(如有元素則返回true,若到達末尾則返回false),Reset方法是將遊標重置到第一項的位置blog

int[] IntArray = new int[] { 1, 2, 3 };

foreach (int n in IntArray)
{
    Console.WriteLine(n);
}

// 上面的foreach等價於下方的代碼實現
IEnumerator e = IntArray.GetEnumerator();
try
{
    while (e.MoveNext())
    {
        Console.WriteLine((int)e.Current);
    }
}
finally
{
    var disposable = e as IDisposable;
    if (disposable != null)
    {
        disposable.Dispose();
    }
}

 

在C#1.0中,建立IEnumerator枚舉器須要寫大量的代碼。C#2.0添加了yield語句,讓編譯器自動建立IEnumerator枚舉器。接口

yield return語句返回集合的一個元素,並移動到下一個元素上。yield break可中止迭代。get

 

非泛型版本IEnumerator

class EnumeratorTest
{
    public IEnumerator GetEnumerator()
    {
        yield return 100;
        yield return "Good";
    }
}

EnumeratorTest eTest = new EnumeratorTest();
foreach (object o in eTest)
{
    Console.WriteLine(o);
}

編譯後生成的代碼以下:

class EnumeratorTest
{
    public IEnumerator GetEnumerator()
    {
        return new GenerateEnumerator(0);
    }

    private sealed class GenerateEnumerator : IEnumerator<object>, IEnumerator, IDisposable
    {
        // Fields
        private int  state;
        private object current;

        // Methods
        public GenerateEnumerator(int state)
        {
            this.state = state;
        }

        bool IEnumerator.MoveNext()
        {
            switch (this.state)
            {
                case 0:
                    this.state = -1;
                    this.current = 100;
                    this.state = 1;
                    return true;

                case 1:
                    this.state = -1;
                    this.current = "Good";
                    this.state = 2;
                    return true;

                case 2:
                    this.state = -1;
                    break;
            }
            return false;
        }

        void IEnumerator.Reset()
        {
            throw new NotSupportedException();
        }

        void IDisposable.Dispose()
        {
        }

        // Properties
        object IEnumerator<object>.Current
        {
            get
            {
                return this.current;
            }
        }
        object IEnumerator.Current
        {
            get
            {
                return this.current;
            }
        }
    }
}

EnumeratorTest eTest = new EnumeratorTest();
IEnumerator e = eTest.GetEnumerator();
try
{
    while (e.MoveNext())
    {
        Console.WriteLine(e.Current);
    }
}
finally
{
    var disposable = e as IDisposable;
    if (disposable != null)
    {
        disposable.Dispose();
    }
}

泛型版本IEnumerator<T>

class EnumeratorTest2
{
    private bool m_bBreak;

    public EnumeratorTest2(bool bBreak)
    {
        m_bBreak = bBreak;
    }

    public IEnumerator<string> GetEnumerator()
    {
        yield return "Hello";
        if (m_bBreak)
        {
            yield break;
        }
        yield return "World";
    }
}

EnumeratorTest2 eTest2 = new EnumeratorTest2(true);
IEnumerator<string> e2 = eTest2.GetEnumerator();
while (e2.MoveNext())
{
    Console.WriteLine(e2.Current);
}

編譯後生成的代碼以下:

class EnumeratorTest2
{
    private bool m_bBreak;

    public EnumeratorTest2(bool bBreak)
    {
        m_bBreak = bBreak;
    }

    public IEnumerator<string> GetEnumerator()
    {
        GenerateEnumerator2 e2 = new GenerateEnumerator2(0);
        e2.eTest2 = this;
        return e2;
    }

    private sealed class GenerateEnumerator2 : IEnumerator<string>, IEnumerator, IDisposable
    {
        // Fields
        private int state;
        private string current;

        public EnumeratorTest2 eTest2;

        // Methods
        public GenerateEnumerator2(int state)
        {
            this.state = state;
        }

        bool IEnumerator.MoveNext()
        {
            switch (this.state)
            {
                case 0:
                    this.state = -1;
                    this.current = "Hello";
                    this.state = 1;
                    return true;

                case 1:
                    this.state = -1;
                    if (eTest2.m_bBreak)
                    {
                        break;
                    }
                    this.current = "World";
                    this.state = 2;
                    return true;

                case 2:
                    this.state = -1;
                    break;
            }
            return false;
        }

        void IEnumerator.Reset()
        {
            throw new NotSupportedException();
        }

        void IDisposable.Dispose()
        {
        }

        // Properties
        string IEnumerator<string>.Current
        {
            get
            {
                return this.current;
            }
        }
        object IEnumerator.Current
        {
            get
            {
                return this.current;
            }
        }
    }
}

EnumeratorTest2 eTest2 = new EnumeratorTest2(true);
IEnumerator<string> e2 = eTest2.GetEnumerator();
while (e2.MoveNext())
{
    Console.WriteLine(e2.Current);
}

非泛型版本IEnumerable

class EnumeratorTest3
{
    public IEnumerable Test1()
    {
        yield return 100;
        yield return 200;
    }
}

EnumeratorTest3 eTest3 = new EnumeratorTest3();
foreach (object o in eTest3.Test1())
{
    Console.WriteLine(o);
}

編譯後生成的代碼以下:

class EnumeratorTest3
{
    public IEnumerable Test1()
    {
        return new GenerateEnumerable3(0);
    }

    private sealed class GenerateEnumerable3 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable
    {
        // Fields
        private int state;
        private int current;

        // Methods
        public GenerateEnumerable3(int state)
        {
            this.state = state;
        }

        bool IEnumerator.MoveNext()
        {
            switch (this.state)
            {
                case 0:
                    this.state = -1;
                    this.current = 100;
                    this.state = 1;
                    return true;

                case 1:
                    this.state = -1;
                    this.current = 200;
                    this.state = 2;
                    return true;

                case 2:
                    this.state = -1;
                    break;
            }
            return false;
        }

        IEnumerator<object> IEnumerable<object>.GetEnumerator()
        {
            return this;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this;
        }

        void IEnumerator.Reset()
        {
            throw new NotSupportedException();
        }

        void IDisposable.Dispose()
        {
        }

        // Properties            
        object IEnumerator<object>.Current
        {
            get
            {
                return this.current;
            }
        }
        object IEnumerator.Current
        {
            get
            {
                return this.current;
            }
        }
    }
}

EnumeratorTest3 eTest3 = new EnumeratorTest3();
IEnumerator e3 = eTest3.Test1().GetEnumerator();
while (e3.MoveNext())
{
    Console.WriteLine(e3.Current);
}

泛型版本IEnumerable<T>

class EnumeratorTest4
{
    private bool m_bBreak;

    public EnumeratorTest4(bool bBreak)
    {
        m_bBreak = bBreak;
    }

    public IEnumerable<float> Test1()
    {
        yield return 1.0f;
        if (m_bBreak)
        {
            yield break;
        }
        yield return 3.0f;
    }
}

EnumeratorTest4 eTest4 = new EnumeratorTest4(true);
foreach (object o in eTest4.Test1())
{
    Console.WriteLine(o);
}

編譯後生成的代碼以下: 

class EnumeratorTest4
{
    private bool m_bBreak;

    public EnumeratorTest4(bool bBreak)
    {
        m_bBreak = bBreak;
    }

    public IEnumerable<float> Test1()
    {
        GenerateEnumerable4 e4 = new GenerateEnumerable4(0);
        e4.eTest4 = this;
        return e4;
    }

    private sealed class GenerateEnumerable4 : IEnumerable<float>, IEnumerable, IEnumerator<float>, IEnumerator, IDisposable
    {
        // Fields
        private int state;
        private float current;

        public EnumeratorTest4 eTest4;

        // Methods
        public GenerateEnumerable4(int state)
        {
            this.state = state;
        }

        bool IEnumerator.MoveNext()
        {
            switch (this.state)
            {
                case 0:
                    this.state = -1;
                    this.current = 1.0f;
                    this.state = 1;
                    return true;

                case 1:
                    this.state = -1;
                    if (this.eTest4.m_bBreak)
                    {
                        break;
                    }
                    this.current = 3.0f;
                    this.state = 2;
                    return true;

                case 2:
                    this.state = -1;
                    break;
            }
            return false;
        }

        IEnumerator<float> IEnumerable<float>.GetEnumerator()
        {
            return this;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this;
        }

        void IEnumerator.Reset()
        {
            throw new NotSupportedException();
        }

        void IDisposable.Dispose()
        {
        }

        // Properties            
        float IEnumerator<float>.Current
        {
            get
            {
                return this.current;
            }
        }
        object IEnumerator.Current
        {
            get
            {
                return this.current;
            }
        }
    }
}

EnumeratorTest4 eTest4 = new EnumeratorTest4(true);
IEnumerator<float> e4 = eTest4.Test1().GetEnumerator();
while (e4.MoveNext())
{
    Console.WriteLine(e4.Current);
}
相關文章
相關標籤/搜索