首页 > 技术文章 > C#基础--迭代器初识

AD-milk 2020-03-11 10:29 原文

foreach语句是枚举器(enumerator)的消费者,而迭代器(iterator)是枚举器的产生者。

迭代器模式能提供一种顺序访问一个集合内部的元素,而又不会暴露其内部的方法。当然其缺点就是用foreach语句遍历的同时,不能修改集合内部的元素。

我们已经在foreach语句中接触过了它 foreach (var item in collection) ,C#利用foreach实现了访问迭代器的内置支持。

实际上foreach被编译后会产生GetEnumerator和MoveNext方法,还有current属性。

一.C#2 便捷的语法糖

下面我们先来介绍C#2.0为实现迭代器提供的便捷语法。

先看两个单词的翻译 enumerator(枚举器) enumerable(可枚举类型)

下面举个例子:

using System;
using System.Collections;
using System.Collections.Generic;

namespace ConsoleApp1
{
    
    class Program
    {
        static IEnumerable<string> AddAB(int Count)
        {
            string str = "h";
            for (int i = 0 ; i < Count; i++)
            {
                yield return str;
                str += "a";
            }
            Console.WriteLine("");
            for (int i = 0 ; i < Count; i++)
            {
                Console.WriteLine("before yoeld return "+str);
                yield return str;
                str += "b";
            }
        }
        static void Main(string[] args)
        {
            foreach (var str in AddAB(3))
            {
                Console.WriteLine(str);
            }
        }
    }
}

 

IEnumerable接口的GetEnumerator方法实现了IEnumerator枚举器类的实例。所以上文的代码是没问题的,后面会给大家示范用IEnumertor接口实现迭代器。

yield return语句的意思是你向我请求从枚举器产生的下一个元素。

每次执行到yield return就会返回到他调用者那,但还会执行yield return之后的语句,直到碰到下一条yield return时停止。这个状态一直持续到foreach语句的结束。

二.C#1 手写迭代器

再来看看这句话:foreach语句是枚举器(enumerator)的消费者,而迭代器(iterator)是枚举器的产生者。
【enumerator(枚举器) enumerable(可枚举类型)】

foreach需要获得一个enumerator,而我们需要用IEnumerator接口实现迭代器。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    class Alp : IEnumerable
    {
        string[] value = { "a", "b", "c" };
        public IEnumerator GetEnumerator()
        {
            return new AlpEnumerator(value);
        }
    }
    class AlpEnumerator : IEnumerator
    {
        string[] alp;
        int position = -1;
        public AlpEnumerator(string[] str)
        {
            alp = new string[str.Length];
            for (int i = 0; i < str.Length; i++)
            {
                alp[i] = str[i];
            }
        }
        public object Current
        {
            get 
            {
                if (position == -1)
                    throw new InvalidOperationException();
                if (position >= alp.Length)
                    throw new InvalidOperationException();
                return alp[position];
            }
        }

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

        public void Reset()
        {
            position = -1;
        }
    }
    class Program
    {
        
        static void Main(string[] args)
        {
            Alp alp = new Alp();
            foreach (var item in alp)
            {
                Console.WriteLine(item);
            }
        }
    }
}
  • Current      获取当前位置
  • MoveNext  判断是否可移动到下一位
  • Reset         将位置重置为初始位置

 

推荐阅读