C#迭代器和接口IEnumerable,IEnumerator

一.迭代器

学过C++的都知道,迭代器是STL中的一个类,用来遍历容器。

vector<int>::iterator iter;
for (iter = v1.begin(); iter != v1.end(); iter++)

C#中迭代器也是用来遍历集合,本质上是一个类/接口(IEnumerator),可以解决在不知道集合以何种key存储的情况下,对集合进行遍历

int[] nums = {1, 2, 3, 4, 5};
foreach(int i in nums)
{
    Console.WriteLine(i);
}

可以看到C#的迭代器i,没有初始值,结束条件,变化的情况;这是因为C#中的foreach隐藏了迭代器的复杂性,foreach是一种语法糖。可以用foreach遍历的集合必须实现接口IEnumerable,IEnumerator.

二.IEnumerator接口

public interface IEnumerator
{
    bool MoveNext();
    object Current { get; }
    void Reset();
}
  • MoveNext将当前元素向前移动到下一个位置,如果集合没有更多元素,那么它会返回false。
  • Current返回当前位置元素。在取得第一个元素之前 必须先调用MoveNext,即使空集合也支持该操作。
  • Reset作用就是当前位置移回起点,并允许再一次枚举集合。此方法一般并不建议使用,因为完全可以重新实例化一个枚举器。

三.IEnumerable接口

public interface IEnumerable: 
{
    IEnumerator GetEnumerator();
}

通过GetEnumerator返回枚举器,IEnumerable可以看作IEnumerator的提供者。

下面例子演示foreach是一种语法糖,本质是调用接口IEnumerator的MoveNext方法和Current属性

class MainClass
    {
        static void Main()
        {
            string s = "Hello";
            IEnumerator<char> iter = s.GetEnumerator(); //通过GetEnumerator方法获取迭代器
            while (iter.MoveNext())                     //通过MoveNext方法判断是否还有下一个元素,并向后移动
            {
                char c = (char)iter.Current;            //通过Current属性获取元素
                Console.Write(c + "_");
            }
            Console.WriteLine();

            foreach(char c in s)
            {
                Console.Write(c + ".");
            }
            Console.WriteLine();

        }
    }
    /* Output:
        H_e_l_l_o
        H.e.l.l.o
    */

四.遍历自定义集合的做法

下面的代码示例演示自定义集合的IEnumerable和IEnumerator接口的实现。 在此示例中,不会显式调用这些接口的成员,它们实现为支持使用foreach循环访问集合。

using System;
using System.Collections;

// Simple business object.
public class Person
{
    public Person(string fName, string lName)
    {
        this.firstName = fName;
        this.lastName = lName;
    }

    public string firstName;
    public string lastName;
}

// Collection of Person objects. This class
// implements IEnumerable so that it can be used
// with ForEach syntax.
public class People : IEnumerable
{
    private Person[] _people;
    public People(Person[] pArray)
    {
        _people = new Person[pArray.Length];

        for (int i = 0; i < pArray.Length; i++)
        {
            _people[i] = pArray[i];
        }
    }

// Implementation for the GetEnumerator method.
    IEnumerator IEnumerable.GetEnumerator()
    {
       return (IEnumerator) GetEnumerator();
    }

    public PeopleEnum GetEnumerator()
    {
        return new PeopleEnum(_people);
    }
}

// When you implement IEnumerable, you must also implement IEnumerator.
public class PeopleEnum : IEnumerator
{
    public Person[] _people;

    // Enumerators are positioned before the first element
    // until the first MoveNext() call.
    int position = -1;

    public PeopleEnum(Person[] list)
    {
        _people = list;
    }

    public bool MoveNext()
    {
        position++;
        return (position < _people.Length);
    }

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

    object IEnumerator.Current
    {
        get
        {
            return Current;
        }
    }

    public Person Current
    {
        get
        {
            try
            {
                return _people[position];
            }
            catch (IndexOutOfRangeException)
            {
                throw new InvalidOperationException();
            }
        }
    }
}

class App
{
    static void Main()
    {
        Person[] peopleArray = new Person[3]
        {
            new Person("John", "Smith"),
            new Person("Jim", "Johnson"),
            new Person("Sue", "Rabon"),
        };

        People peopleList = new People(peopleArray);
        foreach (Person p in peopleList)
            Console.WriteLine(p.firstName + " " + p.lastName);
    }
}

/* This code produces output similar to the following:
 *
 * John Smith
 * Jim Johnson
 * Sue Rabon
 *
 */

五.IEnumerable返回IEnumerator的作用

先说结论,如果集合实现IEnumerator的话,存在问题。

当出现循环嵌套时,内循环跳出时会把index制为-1,外循环永远无法结束,造成死循环。

为了解决这一问题,IEnumerable的GetEnumerator方法会返回一个new出来的IEnumerator对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ellis1970

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值