有人能给我解释一下IEnumerable和IEnumerator吗?

例如,什么时候用它胜过foreach?IEnumerable和IEnumerator的区别是什么?为什么我们需要使用它?


当前回答

例如,什么时候用它胜过foreach?

你不用IEnumerable "over" foreach。实现IEnumerable使得使用foreach成为可能。

当你写这样的代码时:

foreach (Foo bar in baz)
{
   ...
}

它在功能上相当于这样写:

IEnumerator bat = baz.GetEnumerator();
while (bat.MoveNext())
{
   bar = (Foo)bat.Current
   ...
}

所谓“功能等效”,我指的是编译器实际将代码转换成的内容。在本例中,除非baz实现了IEnumerable,否则不能在baz上使用foreach。

IEnumerable表示baz实现了该方法

IEnumerator GetEnumerator()

该方法返回的IEnumerator对象必须实现这些方法

bool MoveNext()

and

Object Current()

第一个方法前进到创建枚举器的IEnumerable对象中的下一个对象,如果完成则返回false,第二个方法返回当前对象。

. net中任何你可以迭代的东西都实现了IEnumerable。如果您正在构建自己的类,并且它还没有从实现了IEnumerable的类继承,那么您可以通过实现IEnumerable(并通过创建其新的GetEnumerator方法将返回的枚举器类)使您的类在foreach语句中可用。

其他回答

实现IEnumerable本质上意味着可以遍历对象。这并不一定意味着它是一个数组,因为有一些列表不能被索引,但你可以枚举它们。

IEnumerator是用于执行迭代的实际对象。它控制从一个对象移动到列表中的下一个对象。

大多数情况下,IEnumerable和IEnumerator被透明地用作foreach循环的一部分。

IEnumerable和IEnumerator(以及它们的泛型对应物IEnumerable<T>和IEnumerator<T>)是. net Framework类库集合中迭代器实现的基本接口。

IEnumerable是大多数代码中最常见的接口。它支持foreach循环,生成器(考虑yield),由于它的小接口,它被用于创建紧密的抽象。IEnumerable依赖于IEnumerator。

另一方面,IEnumerator提供了一个稍低级别的迭代接口。它被称为显式迭代器,使程序员能够更好地控制迭代周期。

IEnumerable

IEnumerable是一个标准接口,支持对支持它的集合进行迭代(事实上,我现在能想到的所有集合类型都实现了IEnumerable)。编译器支持允许像foreach这样的语言特性。一般来说,它支持这个隐式迭代器实现。

foreach循环

foreach (var value in list)
  Console.WriteLine(value);

我认为foreach循环是使用IEnumerable接口的主要原因之一。foreach的语法非常简洁,与经典C风格的for循环相比非常容易理解,在经典C风格的for循环中,您需要检查各种变量来查看它在做什么。

生成的关键字

可能一个不太为人所知的特性是IEnumerable还通过使用yield return和yield break语句支持c#中的生成器。

IEnumerable<Thing> GetThings() {
   if (isNotReady) yield break;
   while (thereIsMore)
     yield return GetOneMoreThing();
}

抽象

实践中的另一个常见场景是使用IEnumerable来提供极简抽象。因为它是一个很小的只读接口,所以建议将集合公开为IEnumerable(而不是List)。这样你就可以在不破坏客户端代码的情况下自由更改实现(例如将List更改为LinkedList)。

问题

需要注意的一种行为是,在流实现中(例如,从数据库中逐行检索数据,而不是首先将所有结果加载到内存中),您不能对集合进行多次迭代。这与List之类的内存集合相反,在List中可以迭代多次而不会出现问题。例如,ReSharper对IEnumerable的可能多个枚举进行了代码检查。

分子

另一方面,IEnumerator是使ienumble -foreach-magic工作的幕后接口。严格来说,它启用了显式迭代器。

var iter = list.GetEnumerator();
while (iter.MoveNext())
    Console.WriteLine(iter.Current);

根据我的经验,IEnumerator很少在常见场景中使用,因为它的语法比较冗长,语义有点混乱(至少对我来说是这样;例如,MoveNext()也返回一个值,这个名称根本不建议)。

IEnumerator的用例

我只在提供IEnumerable接口的库和框架中使用IEnumerator(级别稍低)。一个例子是数据流处理库,它在foreach循环中提供了一系列对象,即使在幕后数据是使用各种文件流和序列化收集的。

客户端代码

foreach(var item in feed.GetItems())
    Console.WriteLine(item);

图书馆

IEnumerable GetItems() {
    return new FeedIterator(_fileNames)
}

class FeedIterator: IEnumerable {
    IEnumerator GetEnumerator() {
        return new FeedExplicitIterator(_stream);
    }
}

class FeedExplicitIterator: IEnumerator {
    DataItem _current;

    bool MoveNext() {
        _current = ReadMoreFromStream();
        return _current != null;           
    }

    DataItem Current() {
        return _current;   
    }
}

理解Iterator模式将对您有所帮助。我建议你也读一下。

迭代器模式

在高层次上,迭代器模式可用于提供一种遍历任何类型集合的标准方法。 迭代器模式中有3个参与者,实际的集合(客户端)、聚合器和迭代器。聚合是一个接口/抽象类,具有返回迭代器的方法。Iterator是一个接口/抽象类,它具有允许我们遍历集合的方法。

为了实现该模式,我们首先需要实现一个迭代器来生成一个具体的迭代器,该迭代器可以遍历相关的集合(客户端) 然后集合(客户端)实现聚合器以返回上述迭代器的实例。

下面是UML图

在c#中,IEnumerable是抽象的集合IEnumerator是抽象的迭代器。IEnumerable有一个单独的方法GetEnumerator,它负责创建所需类型的IEnumerator实例。像list这样的集合实现了IEnumerable。

的例子。 假设我们有一个getPermutations(inputString)方法,它返回一个字符串的所有排列,并且该方法返回IEnumerable<string>的实例

为了计算排列的数量,我们可以像下面这样做。

 int count = 0;
        var permutations = perm.getPermutations(inputString);
        foreach (string permutation in permutations)
        {
            count++;
        }

c#编译器或多或少地将上面的转换为

using (var permutationIterator = perm.getPermutations(input).GetEnumerator())
        {
            while (permutationIterator.MoveNext())
            {
                count++;
            }
        }

如果你有任何问题,请尽管问。

IEnumerable是一个包含Ienumerator的框。IEnumerable是所有集合的基本接口。如果集合实现了IEnumerable,则foreach循环可以运行。在下面的代码中,它解释了拥有自己的枚举器的步骤。让我们首先定义我们要创建集合的Class。

public class Customer
{
    public String Name { get; set; }
    public String City { get; set; }
    public long Mobile { get; set; }
    public double Amount { get; set; }
}

现在我们将定义Class,它将作为我们的类Customer的集合。注意,它实现了接口IEnumerable。所以我们必须实现GetEnumerator方法。这将返回我们的自定义枚举器。

public class CustomerList : IEnumerable
{
    Customer[] customers = new Customer[4];
    public CustomerList()
    {
        customers[0] = new Customer { Name = "Bijay Thapa", City = "LA", Mobile = 9841639665, Amount = 89.45 };
        customers[1] = new Customer { Name = "Jack", City = "NYC", Mobile = 9175869002, Amount = 426.00 };
        customers[2] = new Customer { Name = "Anil min", City = "Kathmandu", Mobile = 9173694005, Amount = 5896.20 };
        customers[3] = new Customer { Name = "Jim sin", City = "Delhi", Mobile = 64214556002, Amount = 596.20 };
    }

    public int Count()
    {
        return customers.Count();
    }
    public Customer this[int index]
    {
        get
        {
            return customers[index];
        }
    }
    public IEnumerator GetEnumerator()
    {
        return customers.GetEnumerator(); // we can do this but we are going to make our own Enumerator
        return new CustomerEnumerator(this);
    }
}

现在我们要创建自己的自定义枚举器,如下所示。我们必须实现方法MoveNext。

 public class CustomerEnumerator : IEnumerator
    {
        CustomerList coll;
        Customer CurrentCustomer;
        int currentIndex;
        public CustomerEnumerator(CustomerList customerList)
        {
            coll = customerList;
            currentIndex = -1;
        }

        public object Current => CurrentCustomer;

        public bool MoveNext()
        {
            if ((currentIndex++) >= coll.Count() - 1)
                return false;
            else
                CurrentCustomer = coll[currentIndex];
            return true;
        }

        public void Reset()
        {
            // we dont have to implement this method.
        }
    }

现在我们可以像下面这样对我们的集合使用foreach循环;

    class EnumeratorExample
    {
        static void Main(String[] args)
        {

            CustomerList custList = new CustomerList();
            foreach (Customer cust in custList)
            {
                Console.WriteLine("Customer Name:"+cust.Name + " City Name:" + cust.City + " Mobile Number:" + cust.Amount);
            }
            Console.Read();

        }
    }

实现IEnumerable的对象允许其他人访问它的每一项(通过枚举器)。 实现IEnumerator的对象是在进行迭代。它循环遍历一个可枚举对象。

可以把可枚举对象想象成列表、堆栈、树。