C#-14 枚举器和迭代器

一 枚举器和可枚举类型

当我们为数组使用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);
    }
}
View Code

三 泛型枚举接口

非泛型接口的实现不是类型安全的,它返回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";
    }
}
View Code

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";
    }
}
View Code
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值