黑马程序员 枚举数和迭代器 总结

来源:互联网 发布:ubuntu安装 分区 编辑:程序博客网 时间:2024/05/18 19:40
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

1.为什么数组可以使用foreach遍历。原因是数组可以按需提供一个叫做枚举数的对象。枚举数可以依次返回请求的数组元素。
我的话说就是:数组实现了一个叫IEnumerable的接口,该接口有一个方法GetEnumerator(); 该方法返回一个实现了IEnumerator接口的对象,利用该对象可以依次取出这个数组的每个值。

2.foreach 结构被设计用来和可枚举类型一起使用。只要给它的遍历对象是可枚举类型,比如数组,它就会执行如下行为:通过调用GetEnumerator方法获取对象的枚举数;
从枚举数中请求每一项把它作为迭代变量,代码可以读但不可以改变。

3.IEnumerator接口
IEnumerator接口包含3个函数成员:Current、MoveNext、以及Reset
Current返回序列中当前位置项的属性。
MoveNext是把枚举位置前进到集合中下一项的方法。它也返回布尔值,指示新的位置是有效位置或已经超过了序列的尾部。

4.IEnumerable接口只有一个成员——GetEnumerator方法,它返回对象的枚举数。

5.我们来看一个自己实现枚举数的例子:
    class ColorEnumerator : IEnumerator
    {
        string[] Colors;
        int Position = -1;

        public ColorEnumerator(string[] theColors)
        {
            Colors = new string[theColors.Length];
            for (int i = 0; i < theColors.Length; i++)
            {
                Colors[i] = theColors[i];
            }

        }

        public object Current
        {
            get
            {
                if(Position == -1 || Position == Colors.Length)
                throw new NotImplementedException();
                return Colors[Position];
            }
        }

        public bool MoveNext()
        {
            if (Position < Colors.Length - 1)
            {
                Position++;
                return true;
            }
            else
                return false;
        }

        public void Reset()
        {
            Position = -1;
        }
    }

    class MyColors : IEnumerable
    {
        string[] Colors = { "Red","Yellow","Blue"};

        public IEnumerator GetEnumerator()
        {
            return new ColorEnumerator(Colors);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyColors mc = new MyColors();
            foreach (string color in mc)
            {
                Console.WriteLine(color);
            }

            Console.ReadKey();
        }
    }

6.IEnumerator<T>接口
IEnumerator<T>接口使用泛型来返回实际的类型,而不是Object类型的引用。
IEnumerator<T>接口从另外两个接口继承——非泛型IEnumerator接口和IDisposable接口。实现成员:
IDisposable 接口只有一个叫做Dispose的类型为void的无参方法,它可以用来释放由类占据的非托管资源
IEnumerator<T>接口本身只有一个Current特性,它返回类型T或它衍生的项的实例——不是ojbect类型的引用。
由于IEnumerator<T>和IEnumerator都有一个叫做Current的成员,我们应该显式实现IEnumerator版本,然后在类中实现泛型版本。

7.IEnumerable<T>接口
泛型版本从IEnumerator继承,所以也必须实现IEnumerable接口
与IEnumerable差不多,泛型版本也包含了一个方法——GetEnumertor。然而,这个版本的GetEnumerator实现泛型IEnumerator<T>接口的类对象。
由于类必须实现两个GetEnumerator方法,我们需要显式实现非泛型版本,并在类中实现泛型版本。

8.用泛型接口实现的迭代器,示例:
    class ColorEnumerator : IEnumerator<string>
    {
        string[] Colors;
        int Position = -1;

        public ColorEnumerator(string[] theColors)
        {
            Colors = new string[theColors.Length];
            for (int i = 0; i < theColors.Length; i++)
            {
                Colors[i] = theColors[i];
            }

        }

        object IEnumerator.Current
        {
            get
            {
                if(Position == -1 || Position == Colors.Length)
                throw new NotImplementedException();
                return Colors[Position];
            }
        }

        public bool MoveNext()
        {
            if (Position < Colors.Length - 1)
            {
                Position++;
                return true;
            }
            else
                return false;
        }

        public void Reset()
        {
            Position = -1;
        }

        public void Dispose()
        {
            //throw new NotImplementedException();
        }

        public string  Current
        {
            get
            {
                if (Position == -1 || Position == Colors.Length)
                    throw new NotImplementedException();
                return Colors[Position];
            }
        }
    }

    class MyColors : IEnumerable<string>
    {
        string[] Colors = { "Red","Yellow","Blue"};

        public IEnumerator<string> GetEnumerator()
        {
            return new ColorEnumerator(Colors);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return new ColorEnumerator(Colors);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyColors mc = new MyColors();
            foreach (string color in mc)
            {
                Console.WriteLine(color);
            }

            Console.ReadKey();
        }
    }

9.迭代器:
迭代器就是为我们简化创建迭代数的东西,我们不用再外写一个实现IEnumerator接口的枚举器了,现在只要在我们的类中简单写一个方法可以返回IEnumerator类型(使用yield return 这叫迭代器),再实现IEnumerable接口, GetEnumerator方法中调迭代器就可。 实际上,这是编译器帮我们简化了,再编译时编译器还是创建了枚举器。
 
迭代器示例:
    class Myclass
    {

        public IEnumerator<string> GetEnumerator()
        {
           return BlackAndWhite();
        }

        public IEnumerator<string> BlackAndWhite()
        {
            yield return "black";
            yield return "glay";
            yield return "white";
        }

    }

    class Program
    {
        static void Main(string[] args)
        {
            Myclass myC = new Myclass();

            foreach (string str in myC)
            {
                Console.WriteLine(str);
            }

            Console.ReadKey();
        }
    }
其实foreach迭代时,那个类只需有GetEnumerator方法(public 无参)就行,所以自己创建枚举器时,类名后也可以不加 : IEnumerable。   还可以使用迭代器来创建可枚举类型,BlackAndWhite还可以返回IEnumerable<string>,等各种玩法。在foreach中还可以直接调用BlackAndWhite方法,因为它返回的是可枚举类型,如下:
    class Myclass
    {

        public IEnumerator<string> GetEnumerator()
        {
            return BlackAndWhite().GetEnumerator();
        }

        public IEnumerable<string> BlackAndWhite()
        {
            yield return "black";
            yield return "glay";
            yield return "white";
        }

    }

    class Program
    {
        static void Main(string[] args)
        {
            Myclass myC = new Myclass();

            foreach (string str in myC)
            {
                Console.WriteLine(str);
            }

            foreach (string str in myC.BlackAndWhite())
            {
                Console.WriteLine(str);
            }

            Console.ReadKey();
        }
    }
比较两种迭代模式:
1.如果实现GetEnumerator,它调用迭代器方法以获取自动生成的实现IEnumerable的类实例。然后,从IEnumerator对象返回由GetEnumerable对象返回由GetEumerator创建的枚举数。
2.如果通过不实现GetEnumerator使类本身不可被枚举,仍然可以使用由迭代器返回的可枚举类,只需要直接调用迭代器方法。
我们可使用第2种模式不实现GetEnumerator,直接调用迭代器方法可以写多个可枚举类型。
还可以使用属性产生多个枚举数:
{
    class Myclass :IEnumerable<string>
    {
        bool ColorFlag = false;

        public Myclass(bool flag)
        {
            ColorFlag = flag;
        }

        IEnumerator<string> BlackAndWhite
        {
            get
            {
                yield return "black";
                yield return "glay";
                yield return "white";
            }
        }

        IEnumerator<string> Colors
        {
            get
            {
                string[] theColors = { "blue", "red", "yellow"};
                for(int i=0;i<theColors.Length;i++)
                    yield return theColors[i];
            }
        }

        public IEnumerator<string> GetEnumerator()
        {
            return ColorFlag
                ? Colors
                : BlackAndWhite;
        }


        IEnumerator IEnumerable.GetEnumerator()  //IEnumerator需要引用命名空间System.Collections;
        {
            return ColorFlag
                ? Colors
                : BlackAndWhite;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Myclass myC = new Myclass(true);

            foreach (string str in myC)
            {
                Console.WriteLine(str);
            }

            Console.ReadKey();
        }
    }

10.迭代器实质
由编译器生成的枚举数类是有4个状态的状态机。
Before 首次调用MoveNext的初始状态
Running 调用MoveNext后时入这个状态。在这个状态中,枚举数检测并设置下一项的位置,在遇到yield rturn 、yield brdak或在迭代器体结束时,退出状态。
Suspended 状态机等待下次调用MoveNext的状态。
After 没有更多项可以枚举

---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------