C#深入理解枚举器和迭代器

C#深入理解枚举器和迭代器

1、枚举器和可枚举类型

可枚举类型是实现IEnumerable接口的类型,

    public interface IEnumerable
    {
        IEnumerator GetEnumerator();
    }

IEnumerable接口中有一个方法,用于返回枚举器,枚举器是实现了IEnumerator接口的类型。

IEnumerator接口定义如下:

   //
    // 摘要:
    //     支持对非泛型集合的简单迭代。
    [ComVisible(true)]
    [Guid("496B0ABF-CDEE-11d3-88E8-00902754C43A")]
    public interface IEnumerator
    {
        //
        // 摘要:
        //     获取集合中位于枚举数当前位置的元素。
        //
        // 返回结果:
        //     集合中位于枚举数当前位置的元素。
        object Current { get; }

        //
        // 摘要:
        //     将枚举数推进到集合的下一个元素。
        //
        // 返回结果:
        //     如果枚举数已成功地推进到下一个元素,则为 true;如果枚举数传递到集合的末尾,则为 false。
        //
        // 异常:
        //   T:System.InvalidOperationException:
        //     集合在枚举器创建后被修改。
        bool MoveNext();
        //
        // 摘要:
        //     将枚举数设置为其初始位置,该位置位于集合中第一个元素之前。
        //
        // 异常:
        //   T:System.InvalidOperationException:
        //     集合在枚举器创建后被修改。
        void Reset();
    }

举例说明:

/// 
    /// 自定义一个Colors类,实现IEnumbale接口
    /// 
    public class Colors: IEnumerable
    {
        string[] colors = new string[] { "Red", "Yellow", "Blue", "Green", "orange" };

        /// 
        /// 接口的实现,这里需要返回一个枚举器。
        /// 理论上需要自己实现一个枚举器(实现IEnumerator接口),
        /// 因为这里我们的string[]的父类是Array,它已经实现了IEnumerable接口。所以它自己本身是有一个枚举器的。
        /// 
        /// 返回一个枚举器
        public IEnumerator GetEnumerator()
        {
            return colors.GetEnumerator();
        }
    }

上述代码中,我们实现了IEnumerable接口,那么它就是一个可枚举类型,则可以使用foreach进行枚举。

 static void Main(string[] args)
{
     Colors colors = new Colors();
     foreach (var item in colors)
     {
         Console.WriteLine(item);
     }

     Console.ReadKey();
}

但是,只要一个类中有一个public的IEnumerator GetEnumerator()的方法,那么它就是一个可枚举的类型,并不一定需要实现IEnumerable接口,那么意味Colors类可以改写为这样子:

/// 
    /// 自定义一个Colors类,实现IEnumbale接口
    /// 
    public class Colors
    {
        string[] colors = new string[] { "Red", "Yellow", "Blue", "Green", "orange" };

        /// 
        /// 接口的实现,这里需要返回一个枚举器。
        /// 理论上需要自己实现一个枚举器(实现IEnumerator接口),
        /// 因为这里我们的string[]的父类是Array,它已经实现了IEnumerable接口。所以它自己本身是有一个枚举器的。
        /// 
        /// 返回一个枚举器
        public IEnumerator GetEnumerator()
        {
            return colors.GetEnumerator();
        }
    }

那么,对于可枚举类型的定义可以改为:实现IEnumerator GetEnumerator()方法的类型叫做可枚举类型。

2、foreach的使用

foreach (Type varName in 可枚举类型)
{
        
}

在foreach中我们实际需要的是一个可枚举类型,即实现了IEnumerable接口的类型,这个接口里面有一个枚举器,从枚举器中对每一项进行的迭代。例如Array类型就已经实现了IEnumerable接口。

3、泛型接口

1)IEnumerable接口的GetEnumerator()方法返回实现IEnumerator枚举器类的实例。

2)实现IEnumerator的类的实现了Current属性,它是一个object类型的,然后我们必须把它转换为实际类型的类型。

3)IEnumerable泛型接口中GetEnumerator()方法返回的也是一个泛型枚举器类型IEnumerator。在

IEnumerator中Current也是一个T类型的,而不是object类型。

4、迭代器(iterator)

迭代器可以简单看作返回IEnumerable或者IEnumerator类型的方法,该方法中使用了两种特殊的语句:

1)yield return,指定了序列中返回的下一项

2)yield break,指定在序列中没有其他项

迭代器的意义就是把手动编码(这里应该值的是实现接口的方式)的可枚举类型和枚举器替换为有迭代器生产的可枚举类型和枚举器。

举例说明:

还是使用上面例子中的类,我们在上面的例子中实际上是使用的Array已经定义好的枚举器,那么我们怎么自己使用迭代器来创建枚举器呢??

public class Colors
    {
        /// 
        /// 实现了GetEnumerator()方法,把Colors类定义为可枚举类型
        /// 
        /// 返回一个枚举器
        public IEnumerator GetEnumerator()
        {
            return this.GetColors();//这里的枚举器是使用迭代器船创建的
        }

        public IEnumerator<string> GetColors()
        {
            yield return "red";
            yield return "yellow";
            yield return "blue";
            yield return "green";
            yield return "orange";
        }
    }

其中迭代器改成这样也是可以的:

 public IEnumerator<string> GetColors()
 {
      string[] colors = new string[] { "red", "yellow", "blue", "green", "orange" };

      for (int i = 0; i < colors.Length; i++)
      {
          yield return colors[i];
      }

            //yield return "red";
            //yield return "yellow";
            //yield return "blue";
            //yield return "green";
            //yield return "orange";
 }

再来看看yield break和yield return null的作用:

public IEnumerator<string> GetColors()
 {
       //string[] colors = new string[] { "red", "yellow", "blue", "green", "orange" };

       //for (int i = 0; i < colors.Length; i++)
       //{
       //    if (i == 1)
       //        yield break;
       //    yield return colors[i];
       //}

       yield return "red";
       yield return "yellow";
       yield return null;
       yield return "blue";
       yield break;
       yield return "green";
       yield return "orange";
 }

当遇到yield return null的时候吗,还是会返回null,并且后面的项会继续返回。

当遇到yield break的时候,则会直接跳出,后面的项也不会返回。

那么迭代器创建可枚举类型该怎么使用呢?

直接上代码:

public class Colors
    {
        /// 
        /// 实现了GetEnumerator()方法,把Colors类定义为可枚举类型
        /// 
        /// 返回一个枚举器
        public IEnumerator GetEnumerator()
        {
            IEnumerable<string> e = this.GetColors();

            return e.GetEnumerator();//这里的枚举器是通过定义好的可枚举类型的GetEnumerator()得到的
        }

        /// 
        /// 使用迭代器创建了一个可枚举类型
        /// 
        /// 
        public IEnumerable<string> GetColors()
        {
            string[] colors = new string[] { "red", "yellow", "blue", "green", "orange" };

            for (int i = 0; i < colors.Length; i++)
            {
                yield return colors[i];

            }

            //yield return "red";
            //yield return "yellow";
            //yield return "blue";
            //yield return "green";
            //yield return "orange";
        }
    }

我们可以看到,在迭代器中的代码几乎相同,只是把返回值的类型改为了可枚举类型。然后在GetEnumerator方法中将这个可枚举类型的枚举器返回(也是通过GetEnumerator方法)。对于可枚举类型来说,肯定会有一个GetEnumerator方法用于返回它的枚举器。

其实,如果我们已经在类中实现了迭代器返回一个可枚举类型,那么就没有必要让这个类本身在可枚举了,也就是说没有必要再实现GetEnumerator()方法了,可简化为如下代码:

 public class Colors
    {
 
        /// 
        /// 使用迭代器创建了一个可枚举类型
        /// 
        /// 
        public IEnumerable<string> GetColors()
        {
            string[] colors = new string[] { "red", "yellow", "blue", "green", "orange" };

            for (int i = 0; i < colors.Length; i++)
            {
                yield return colors[i];

            }

            //yield return "red";
            //yield return "yellow";
            //yield return "blue";
            //yield return "green";
            //yield return "orange";
        }
    }

使用的时候就需要这样:

static void Main(string[] args)
        {
            Colors colors = new Colors();

            foreach (var item in colors.GetColors())
            {
                Console.WriteLine(item);
            }

            Console.ReadKey();
        }

5、迭代器总结

1、当我们实现返回枚举器的迭代器时,必须通过GetEnumerator方法来让类可枚举,它返回由迭代器返回的枚举器。

2、如果我们在类中实现迭代器返回可枚举类型,我们可以让类实现GetEnumerator来让类本身可被枚举;或不实现GetEnumerator,让类不可枚举,只需要直接调用迭代器方法进行foreach枚举。

6、创建多个可枚举类型

    public class Colors
    {

        string[] colors = new string[] { "red", "yellow", "blue", "green", "orange" };
        /// 
        /// 使用迭代器创建了一个可枚举类型
        /// 
        /// 
        public IEnumerable<string> GetColorsOne()
        {
            for (int i = 0; i < colors.Length; i++)
            {
                yield return colors[i];

            }
        }

        public IEnumerable<string> GetColorsTwo()
        {
            for (int i = colors.Length - 1; i >= 0; i--)
            {
                yield return colors[i];

            }
        }
    }
    
    
    class Program
    {
        static void Main(string[] args)
        {
            Colors colors = new Colors();

            foreach (var item in colors.GetColorsOne())
            {
                Console.WriteLine(item);
            }
            Console.WriteLine("=========");
            foreach (var item in colors.GetColorsTwo())
            {
                Console.WriteLine(item);
            }

            Console.ReadKey();
        }
    }

7、创建多个枚举器 并把迭代器作为属性

    public enum EnumeratorType
    {
        One,
        Two,
    }

    public class Colors
    {
        EnumeratorType type;
        public Colors(EnumeratorType type)
        {
            this.type = type;
        }

        public IEnumerator<string> GetEnumerator()
        {
            switch (this.type)
            {
                case EnumeratorType.One:
                default:
                    return this.GetColorsOne;
                case EnumeratorType.Two:
                    return this.GetColorsTwo;
            }
        }

        string[] colors = new string[] { "red", "yellow", "blue", "green", "orange" };
        /// 
        /// 使用迭代器创建了一个可枚举类型
        /// 
        /// 
        public IEnumerator<string> GetColorsOne
        {
            get
            {
                for (int i = 0; i < colors.Length; i++)
                {
                    yield return colors[i];

                }
            }
        }

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

                }
            }
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            Colors colors = new Colors(EnumeratorType.One);

            foreach (var item in colors)
            {
                Console.WriteLine(item);
            }

            Console.ReadKey();
        }
    }

你可能感兴趣的:(.Net/C#基础,c#,.net)