可可西

C# 迭代器与yield关键字

迭代器模式是设计模式的一种,因为其运用的普遍性,很多语言都有内嵌的原生支持

在.NET中,迭代器模式是通过IEnumeratorIEnumerable两个接口(有非泛型和泛型2种版本)来封装的

迭代器模式的一个重要方面是:不是一次返回所有数据,而是每次调用只返回一个元素

 

Array、IEnumerable和IEnumerator之间关系如下: 

foreach遍历

① Array、集合容器类派生于IEnumerable接口类(该类中含有一个IEnumerator GetEnumerator()接口函数),Array、集合容器类实现了该函数,这使得foreach可对其进行遍历

    注:不需要从IEnumerable派生,只要类实现了IEnumerator GetEnumerator()函数,就可以使用foreach遍历该类中的元素

② IEnumerator定义了Current属性来返回游标所在的元素,MoveNext方法移动到下一个元素(若有元素则返回true,若到达末尾则返回false),Reset方法是将游标重置到第一项的位置

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可停止迭代。

 

非泛型版本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);
}

 

 

posted on 2019-07-30 12:21  可可西  阅读(1435)  评论(2编辑  收藏  举报

导航