C#笔记(枚举器和迭代器)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38801354/article/details/79522535

1 枚举器

为什么数组可以使用 foreach 输出各元素?

  • 这是因为数组是 枚举类型(enumerable) ,它通过 GetEnumerator 方法提供一个 枚举器(enumerator)即能实现 GetEnumerator 方法的类型是 枚举类型
  • 枚举器可以依次返回请求的数组的元素,枚举器知道 项的次序 并跟踪它的序列位置,然后返回 请求的当前项
  • 对象枚举类型获取枚举器的方法是调用对象的 GetEnumerator 方法

1.1 Ienumerable 接口

可枚举类可以是 实现 Ienumerable 接口的类型,而 Ienumberable 接口只有一个成员,那就是 GetEnumerator 方法,它返回对象的 枚举器

using System.Collections;

// 实现 Ienumerable 接口
class MyColors: IEnumerable
{
    string[] Colors = {"Red", "Yellow", "Blue"};
    public IEnumerator GetEnumerator()
    {
        return new ColorEnumerator(); // 注意上方返回的类型是 IEnumerator,所以这里的返回对象引用会自动转换为它们实现的接口引用
    }
}

1.2 Ienumerator 接口

实现 Ienumerator 接口的枚举器,它包含三个方法:

  • Current:返回序列中当前位置项,属于一个只读 属性
  • MoveNext:把枚举器的位置移动到集合中下一项的方法,返回一个布尔值,有下一项返回 true;另外 枚举器的原始位置在第一项之前(如 -1),即在使用 Current 之前,必须要先调用 MoveNext 方法
  • Reset:把枚举器的位置重置到初始状态的方法

至此,我们也可以写出类如 foreach 语句的功能,如:

static void Main()
{
    int[] MyArray = {10, 11, 12, 13};
    IEnumerator ie = MyArray.GetEnumerator(); // 实现接口,对象引用能自动转换为它们实现的接口引用
    while (ie.MoveNext())
    {
        int i = (int) ie.Current;
        Console.WriteLine("{0}", i);
    }
}

使用 IEnumerableIEnumerator 的实例

using System;
using System.Collections;

// 创建枚举器
class ColorEnumerator : IEnumerator
{
    string[] _colors; // 用于存储内部的 color 数组
    int _position = -1; // 用于表示枚举器位置

    // 构造函数,用于创建 _color 数组
    public ColorEnumerator(string[] theColors)
    {
        _colors = new string[theColors.Length];
        for (int i = 0; i < theColors.Length; i++)
        {
            _colors[i] = theColors[i];
        }
    }

    // 实现 Current
    public object Current
    {
        get
        {
            if (_position == -1)
            {
                throw new InvalidOperationException();
            }
            if (_position >= _colors.Length)
            {
                throw new InvalidOperationException();
            }
            return _colors[_position];
        }
    }

    // 实现MoveNext
    public bool MoveNext()
    {
        if (_position < _colors.Length - 1)
        {
            _position++;
            return true;
        }
        else
        {
            return false;
        }
    }

    // 实现 Reset
    public void Reset()
    {
        _position = -1;
    }
}

// 枚举类型,实现 IEnumrable 接口,提供 GetEnumerator
class Sectrum : IEnumerable
{
    string[] Colors = { "violet", "blue", "cyan", "green" };
    public IEnumerator GetEnumerator()
    {
        return new ColorEnumerator(Colors);
    }
}

// 主流图
class Program
{
    static void Main()
    {
        Sectrum spectrum = new Sectrum();
        foreach (string color in spectrum)
        {
            Console.WriteLine(color);
        }
    }
}

1.3 泛型枚举接口

在大多数情况下,都应该使用泛型版本的 IEnumerator<T>IEnumerable<T>,泛型与非泛型二者的本质都差不多。
对于非泛型而言:

  • IEnumerable 接口的 GetEnumerator 方法返回实现 IEnumerator 枚举类的实例
  • 实现 IEnumerator 的类实现了 Current 属性,它返回 object 的引用,然后我们必须把它转换为实际类型的对象

对于泛型:

  • IEnumerable<T> 接口的 GetEnumerator 方法返回实现 IEnumator<T> 枚举器类的实例
  • 实现 IEnumerator<T> 的类实现了 Current 属性,它返回 实际类型的对象,而不是 object 基类的引用

2 迭代器

C# 在2.0版本开始提供更简单的创建 枚举类型枚举器 的方式,而实际上是 编译器为我们创建它们,这种结构叫做 迭代器,例如:

// 迭代器返回一个泛型枚举器
// 注意由迭代器返回的枚举器并不能完全实现 IEnumerator 接口
// 它没有实现 Reset 方法
public IEnumerator<string> BlackAndWhite()
{
    yield return "black";
    yield return "gray";
    yield return "white";
}

2.1 迭代器块

迭代器块 是有一个或多个 yield 语句的 代码块,下面任意一种都可以是 迭代器块

  • 方法主体
  • 访问器主体
  • 运算符主体

迭代器块其他代码块 不一样:

  • 其他代码块 包含的语句被当做是 命令式,即先执行代码块的第一个语句,然后执行后面的语句,最后控制离开块
  • 迭代器块 不是 需要在同一时间执行的一串命令式命令,而是 描述了希望编译器为我们创建的 枚举器枚举类 的行为
    • yield return 语句指定序列中返回的下一项
    • yield break 语句指定在序列中 没有 其他项

2.2 使用迭代器来创建枚举器

// 并没有实现 IEnumerable 接口
class MyClass
{
    // 实现 GetEnumerator 方法
    public IEnumerator<string> GetNnumerator()
    {
        return BlackAndWhite(); // 返回迭代器生成的泛型枚举器
    }

    // 迭代器返回泛型枚举器(字符串)
    public IEnumerator<string> BlackAndWhite()
    {
        yield return "black";
        yield return "gray";
        yield return "white";
    }
}

class Program
{
    static void Main()
    {
        Myclass mc = new MyClass();
        foreach (string shade in mc) 
        {
            Console.WriteLine(shade);
        }
    }
}

其中要说明的是

  • foreach 只要实现了 GetEnumerator 方法就可以使用 foreach 方法进行遍历
  • 代码中的迭代器产生了一个枚举器,这个枚举器(Enumerator)有一个 嵌套类 实现了 IEnumerator<string>

2.3 使用迭代器来创建可枚举类型


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";
    } 
}

class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        // 使用类对象
        foreach (string shade in mc)
        {
         Console.Write("{0}", shade);
        }
        // 使用类方法
        foreach (string shade in mc.BlackAndWhite())
        {
         Console.Write("{0}", shade);
        }
    }
}

其中编译器生成的类实现了 IEnumerator<string> 也实现了 IEnumerable<string>

2.4 迭代器的实质

  • 迭代器需要 System.Collection.Generic 命名空间,因此我们需要使用 using 指令引入它
  • 在编译器生成枚举器中,Reset 方法没有实现,而它是接口需要的方法

猜你喜欢

转载自blog.csdn.net/qq_38801354/article/details/79522535