关键词搜索

源码搜索 ×
×

C#-14 枚举器和迭代器

发布2022-05-28浏览391次

详情内容

一 枚举器和可枚举类型

当我们为数组使用foreach语句时,这个语句为我们依次取出了数组中的每一个元素。

var arrInt = new int[] { 11, 12, 13, 14 };
foreach (var item in arrInt)
{
    Console.WriteLine(item);
}

原因是数组实现了IEnumerable接口,接口提供了一个GetEnumerator方法可以获取一个实现了IEnumerator接口的枚举器对象。

枚举器可以依次返回请求的数组中的元素。

实现了IEnumerable接口类型叫做可枚举类型。数组是可枚举类型。

public interface IEnumerable
{
    // 摘要: 返回循环访问集合的枚举器。
    // 返回结果:一个可用于循环访问集合的 System.Collections.IEnumerator 对象。
    IEnumerator GetEnumerator();
}

foreach语句设计用来和可枚举类型一起使用。只要给它的遍历对象是可枚举类型,它就会执行如下行为:

  • 通过调用GetEnumerator方法获取对象的枚举器;
  • 从枚举器中请求每一项并把它作为迭代变量。

IEnumerator接口

IEnumerator接口的定义如下:

public interface IEnumerator
{
    // 摘要:获取集合中位于枚举数当前位置的元素
    // 返回结果:集合中位于枚举数当前位置的元素。
    object Current { get; }

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

有了枚举器,就可以使用MoveNext和Current来模仿foreach循环:

int[] arrInt = new int[] { 11, 12, 13, 14 };
foreach (var item in arrInt)
{
    Console.WriteLine(item);
}

var enumerator = arrInt.GetEnumerator();
while (enumerator.MoveNext())
{
    var item = (int)enumerator.Current;
    Console.WriteLine(item);
}

下面是一个使用IEnumerator和IEnumerable的小例子:

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

class ColorEnumeraotr : IEnumerator
{
    string[] _colors;
    int _position = -1;

    public ColorEnumeraotr(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)
                throw new InvalidOperationException();
            if (_position >= _colors.Length)
                throw new InvalidOperationException();

            return _colors[_position];
        }
    }

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

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

class MyColors : IEnumerable 
{
    string[] Colors = new string[] { "red","blue","yellow","green","white"};

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

三 泛型枚举接口

非泛型接口的实现不是类型安全的,它返回object的引用,然后必须转化为实际类型。

所以实际上,在多数情况下,我们应该使用泛型版本的IEnumerable<T>和IEnumerator<T>。

泛型接口的枚举器是类型安全的,它返回实际类型的引用。

IEnumerable<T>接口的GetEnumerator方法返回实现IEnumerator<T>的枚举器对象。

实现IEnumerator<T>的枚举器类的Current属性,它返回T类型的对象。

四 迭代器

C#从2.0版本开始提供了更简单的创建枚举器和可枚举类型的方式。这种结构叫做迭代器。

迭代器块是有一个或多个yield语句的代码块。

迭代器块与其他代码块不同。其他块包含的语句是命令式的,也就是块中的语句依次执行,最后离开块。

而迭代器块是描述了希望编译器为我们创建的枚举器类的行为,迭代器块中的代码描述了如何枚举元素。

迭代器块有两个特殊语句:

  • yield return : 指定了序列中返回的下一项;
  • yield break : 指定在序列中没有其他项。

//产生枚举器的迭代器
public IEnumerator<string> IteratorMethod1() 
{
    yield return "red";
    yield return "blue";
    yield return "yellow";
}

//产生可枚举类型的迭代器
public IEnumerable<string> IteratorMethod2()
{
    yield return "red";
    yield return "blue";
    yield return "yellow";
}

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

下面代码演示如何使用迭代器来创建枚举器:

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

class MyClass 
{
    public IEnumerator<string> GetEnumerator()
    {
        return MyEnumerator();  //返回枚举器
    }

    //迭代器
    public IEnumerator<string> MyEnumerator() 
    {
        yield return "red";
        yield return "blue";
        yield return "yellow";
    }
}

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

下面代码演示如何使用迭代器来创建可枚举类型:

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

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

class MyClass 
{
    public IEnumerator<string> GetEnumerator()
    {
        IEnumerable<string> myEnumerable = MyEnumerable();
        return myEnumerable.GetEnumerator();
    }
    
    public IEnumerable<string> MyEnumerable() 
    {
        yield return "red";
        yield return "blue";
        yield return "yellow";
    }
}

相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载