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

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


当前回答

小贡献。

正如他们中的许多人解释“何时使用”和“与foreach一起使用”。 我想在这里添加另一个状态差异,以询问IEnumerable和IEnumerator之间的差异。

我根据下面的讨论线程创建了下面的代码示例。

IEnumerable, IEnumerator vs foreach,什么时候使用什么 IEnumerator和IEnumerable的区别是什么?

Enumerator保留函数调用之间的状态(迭代位置),而Enumerable则不保留迭代。

下面是带有注释的测试示例。

专家请补充/纠正我。

static void EnumerableVsEnumeratorStateTest()
{
    IList<int> numList = new List<int>();

    numList.Add(1);
    numList.Add(2);
    numList.Add(3);
    numList.Add(4);
    numList.Add(5);
    numList.Add(6);

    Console.WriteLine("Using Enumerator - Remembers the state");
    IterateFrom1to3(numList.GetEnumerator());

    Console.WriteLine("Using Enumerable - Does not Remembers the state");
    IterateFrom1to3Eb(numList);

    Console.WriteLine("Using Enumerable - 2nd functions start from the item 1 in the collection");
}

static void IterateFrom1to3(IEnumerator<int> numColl)
{
    while (numColl.MoveNext())
    {
        Console.WriteLine(numColl.Current.ToString());

        if (numColl.Current > 3)
        {
            // This method called 3 times for 3 items (4,5,6) in the collection. 
            // It remembers the state and displays the continued values.
            IterateFrom3to6(numColl);
        }
    }
}

static void IterateFrom3to6(IEnumerator<int> numColl)
{
    while (numColl.MoveNext())
    {
        Console.WriteLine(numColl.Current.ToString());
    }
}

static void IterateFrom1to3Eb(IEnumerable<int> numColl)
{
    foreach (int num in numColl)
    {
        Console.WriteLine(num.ToString());

        if (num>= 5)
        {
            // The below method invokes for the last 2 items.
            //Since it doesnot persists the state it will displays entire collection 2 times.
            IterateFrom3to6Eb(numColl);
        }
    }
}

static void IterateFrom3to6Eb(IEnumerable<int> numColl)
{
    Console.WriteLine();
    foreach (int num in numColl)
    {
        Console.WriteLine(num.ToString());
    }
}

其他回答

实现IEnumerable可以让你获得一个列表的IEnumerator。

IEnumerator允许使用yield关键字以foreach样式顺序访问列表中的项。

在foreach实现之前(例如在Java 1.4中),迭代列表的方法是从列表中获取一个枚举器,然后向它请求列表中的“下一个”项,只要作为下一个项返回的值不为空。Foreach只是隐式地将其作为一种语言特性来实现,与lock()在幕后实现Monitor类的方式相同。

我期望foreach工作在列表上,因为它们实现了IEnumerable。

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

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

IEnumerable和IEnumerator接口

在开始研究实现现有.NET接口的过程之前,让我们首先看看的角色 IEnumerable和IEnumerator。回想一下,c#支持名为foreach的关键字,它允许您这样做 遍历任意数组类型的内容:

// Iterate over an array of items.
int[] myArrayOfInts = {10, 20, 30, 40};
foreach(int i in myArrayOfInts)
{
   Console.WriteLine(i);
}

虽然看起来只有数组类型可以使用这个构造,但事实是 任何支持名为GetEnumerator()方法的类型都可以由foreach构造求值。来 举例说明,跟我来!

假设我们有一个Garage类:

// Garage contains a set of Car objects.
public class Garage
{
   private Car[] carArray = new Car[4];
   // Fill with some Car objects upon startup.
   public Garage()
   {
      carArray[0] = new Car("Rusty", 30);
      carArray[1] = new Car("Clunker", 55);
      carArray[2] = new Car("Zippy", 30);
      carArray[3] = new Car("Fred", 30);
   }
}

理想情况下,使用foreach迭代Garage对象的子项会很方便 构造,就像一个数据值数组一样:

// This seems reasonable ...
public class Program
{
   static void Main(string[] args)
   {
      Console.WriteLine("***** Fun with IEnumerable / IEnumerator *****\n");
      Garage carLot = new Garage();
      // Hand over each car in the collection?
      foreach (Car c in carLot)
      {
         Console.WriteLine("{0} is going {1} MPH",
         c.PetName, c.CurrentSpeed);
      }
      Console.ReadLine();
   }
}

不幸的是,编译器告诉您Garage类没有实现名为 GetEnumerator()。该方法由IEnumerable接口形式化,该接口隐藏在系统中。集合名称空间。 支持此行为的类或结构宣称它们能够公开所包含的内容 调用者的子项(在本例中为foreach关键字本身)。下面是这个标准。net接口的定义:

// This interface informs the caller
// that the object's subitems can be enumerated.
public interface IEnumerable
{
   IEnumerator GetEnumerator();
}

如您所见,GetEnumerator()方法返回对另一个名为 System.Collections.IEnumerator。该接口提供了允许调用者遍历ienumerable兼容容器所包含的内部对象的基础结构:

// This interface allows the caller to
// obtain a container's subitems.
public interface IEnumerator
{
   bool MoveNext (); // Advance the internal position of the cursor.
   object Current { get;} // Get the current item (read-only property).
   void Reset (); // Reset the cursor before the first member.
}

如果您想要更新Garage类型以支持这些接口,那么您可能要走很长的路 手动实现每个方法。当然,您可以自由地提供自定义版本的 GetEnumerator(), MoveNext(), Current和Reset(),有一个更简单的方法。作为系统。数组类型(以及许多其他集合类)已经实现了IEnumerable和IEnumerator,您可以简单地将请求委托给系统。数组如下:

using System.Collections;
...
public class Garage : IEnumerable
{
   // System.Array already implements IEnumerator!
   private Car[] carArray = new Car[4];
   public Garage()
   {
      carArray[0] = new Car("FeeFee", 200);
      carArray[1] = new Car("Clunker", 90);
      carArray[2] = new Car("Zippy", 30);
      carArray[3] = new Car("Fred", 30);
   }
   public IEnumerator GetEnumerator()
   {
      // Return the array object's IEnumerator.
      return carArray.GetEnumerator();
   }
}

更新车库类型之后,就可以在c# foreach构造中安全地使用该类型了。此外,假定GetEnumerator()方法是公开定义的,对象用户也可以与IEnumerator类型交互:

// Manually work with IEnumerator.
IEnumerator i = carLot.GetEnumerator();
i.MoveNext();
Car myCar = (Car)i.Current;
Console.WriteLine("{0} is going {1} MPH", myCar.PetName, myCar.CurrentSpeed);

但是,如果您希望从对象级别隐藏IEnumerable的功能,只需使 使用显式接口实现:

IEnumerator IEnumerable.GetEnumerator()
{
  // Return the array object's IEnumerator.
  return carArray.GetEnumerator();
}

通过这样做,临时对象用户将不会找到Garage的GetEnumerator()方法,而 Foreach构造将在必要时在后台获取接口。

改编自Pro c# 5.0和。net 4.5框架

IEnumerable和IEnumerator都是c#中的接口。

IEnumerable是一个接口,它定义了一个返回IEnumerator接口的方法GetEnumerator()。

这适用于对集合的只读访问,该集合实现了IEnumerable可与foreach语句一起使用。

IEnumerator有两个方法,MoveNext和Reset。它还有一个名为Current的属性。

下面展示了IEnumerable和IEnumerator的实现。

实现IEnumerable意味着你的类返回一个IEnumerator对象:

public class People : IEnumerable
{
    IEnumerator IEnumerable.GetEnumerator()
    {
        // return a PeopleEnumerator
    }
}

实现IEnumerator意味着你的类返回迭代的方法和属性:

public class PeopleEnumerator : IEnumerator
{
    public void Reset()...

    public bool MoveNext()...

    public object Current...
}

这就是区别所在。