关于c#:任何人都可以向我解释IEnumerable和IEnumerator吗?

Can anyone explain IEnumerable and IEnumerator to me?

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

例如,什么时候在foreach上使用它?IEnumerable和IEnumerator有什么区别?为什么我们需要使用它?


for example, when to use it over foreach?

你不能用IEnumerable代替foreach。实施IEnumerable使使用foreach成为可能。

当你写代码的时候,比如:

1
2
3
4
foreach (Foo bar in baz)
{
   ...
}

它在功能上相当于写:

1
2
3
4
5
6
IEnumerator bat = baz.GetEnumerator();
while (bat.MoveNext())
{
   bar = (Foo)bat.Current
   ...
}

所谓"功能对等",我的意思是编译器实际上就是把代码转换成这样的。在本例中,除非baz实现IEnumerable,否则不能在baz上使用foreach

IEnumerable表示baz实现方法

1
IEnumerator GetEnumerator()

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

1
bool MoveNext()

1
Object Current()

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

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


IEnumerable和IEnumerator接口

要开始检查实现现有.NET接口的过程,首先让我们看看IEnumerable和IEnumerator。回想一下,c支持一个名为foreach的关键字,它允许您迭代任何数组类型的内容:

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

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

假设我们有一个车库班:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 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可以方便地遍历车库对象的子项构造,就像数据值数组一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// This seems reasonable ...
public class Program
{
   static void Main(string[] args)
   {
      Console.WriteLine("***** Fun with IEnumerable / IEnumerator *****
"
);
      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接口形式化,发现它隐藏在System.Collections命名空间中。支持此行为的类或结构声明它们能够公开包含的调用方的子项(在本例中是foreach关键字本身)。以下是此标准.NET接口的定义:

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

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

1
2
3
4
5
6
7
8
// 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.
}

如果你想更新车库类型来支持这些接口,你可以走很长的路,手动实现每个方法。当然,您可以自由地提供getEnumerator()、moveNext()、current和reset(),有一种更简单的方法。由于System.Array类型(以及许多其他集合类)已经实现了IEnumerable和IEnumerator,因此可以简单地将请求委托给System.Array,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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类型交互:

1
2
3
4
5
// 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的功能,只需使用显式接口实现:

1
2
3
4
5
IEnumerator IEnumerable.GetEnumerator()
{
  // Return the array object's IEnumerator.
  return carArray.GetEnumerator();
}

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

改编自Pro C_5.0和.NET 4.5框架


实现IEnumerable意味着您的类返回IEnumerator对象:

1
2
3
4
5
6
7
public class People : IEnumerable
{
    IEnumerator IEnumerable.GetEnumerator()
    {
        // return a PeopleEnumerator
    }
}

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

1
2
3
4
5
6
7
8
public class PeopleEnumerator : IEnumerator
{
    public void Reset()...

    public bool MoveNext()...

    public object Current...
}

不管怎样,这就是区别。


通过类比+代码演练进行解释

首先是一个没有代码的解释,然后我将在后面添加它。

假设你在经营一家航空公司。在每架飞机上,你都想知道有关乘客在飞机上飞行的信息。基本上你想要能够"穿越"平面。换言之,您希望能够从前排开始,然后朝飞机后部走去,询问乘客一些信息:他们是谁,来自哪里等。飞机只能这样做,如果是:

  • 可数的
  • 如果它有计数器。
  • 为什么这些要求?因为这就是接口所需要的。

    如果这是信息过载,你只需要知道,你想能够问飞机上的每一位乘客一些问题,从第一个开始,到最后一个。

    可数是什么意思?

    如果一家航空公司是"可计算的",这意味着飞机上必须有一名空姐在场,他唯一的工作就是计算——而且这个空姐必须以非常具体的方式计算:

  • 柜台/乘务员必须在第一名乘客之前开始(在演示安全、如何穿救生衣等的每个人面前)。
  • 他/她(即乘务员)必须"下一个"沿着过道向上移动到第一个座位。
  • 然后他/她将记录:(i)坐在座位上的人是谁,(ii)他们在过道中的当前位置。
  • 计数程序

    航空公司的机长希望每名乘客在被调查或统计时都有一份报告。因此,在与第一个座位的人交谈后,空姐/柜台向机长报告,当报告发出时,柜台会记住他/她在过道中的确切位置,并继续从他/她离开的地方开始计算。

    以这种方式,船长总是能够掌握被调查人员的信息。这样,如果他发现这个人喜欢曼城,他就可以给那个乘客优惠待遇等。

    • 柜台一直开到他到达飞机的尽头。

    让我们把这个和IEnumerables联系起来

    • 可枚举的只是飞机上乘客的集合。《民用航空法》——这些基本上是所有IEnumerable必须遵守的规则。每次空姐带着乘客信息去机长那里,我们基本上都是把乘客"让给"机长。机长基本上可以对乘客做他想做的任何事情,除了重新安排飞机上的乘客。在这种情况下,如果他们跟随曼城(啊!)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      foreach (Passenger passenger in Plane)
      // the airline hostess is now at the front of the plane
      // and slowly making her way towards the back
      // when she get to a particular passenger she gets some information
      // about the passenger and then immediately heads to the cabin
      // to let the captain decide what to do with it
      { // <---------- Note the curly bracket that is here.
          // we are now cockpit of the plane with the captain.
          // the captain wants to give the passenger free
          // champaign if they support manchester city
          if (passenger.supports_mancestercity())
          {
              passenger.getFreeChampaign();
          } else
          {
              // you get nothing! GOOD DAY SIR!
          }
      } //  <---- Note the curly bracket that is here!
            the hostess has delivered the information
            to the captain and goes to the next person
            on the plane (if she has not reached the
            end of the plane)

    总结

    换言之,如果有计数器的话,有些东西是可数的。柜台必须(基本上):(i)记住它的位置(州),(ii)能够移动到下一个,(iii)知道他正在处理的人。

    "可数"只是"可数"的一个花哨的词。换句话说,可枚举允许您"枚举"(即计数)。


    IEnumerable实现GetEnumerator。调用时,该方法将返回实现moveNext、reset和current的IEnumerator。

    因此,当类实现IEnumerable时,您的意思是可以调用方法(GetEnumerator)并返回一个新的对象(IEnumerator),可以在foreach等循环中使用。


    实现IEnumerable使您能够获取列表的IEnumerator。

    IEnumerator允许foreach风格的顺序访问列表中的项目,使用yield关键字。

    在FACH实现之前(例如,在Java 1.4中),迭代列表的方法是从列表中获取枚举数,然后在列表中要求其为"下一个"项,只要返回的值为NULL NULL.forEach只是隐式地作为一种语言特性来完成这项工作,就像lock()在后台实现monitor类一样。

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


      百万千克1实现IEnumerable的对象允许其他人访问它的每个项(通过枚举器)。百万千克1百万千克1实现IEnumerator的对象是执行迭代。它在一个可枚举对象上循环。百万千克1

    把可枚举对象看作是列表、堆栈、树。


    IEnumerable和IEnumerator(及其通用对应项IEnumerable和IEnumerator)是.NET框架类库集合中迭代器实现的基本接口。

    IEnumerable是大多数代码中最常见的接口。它支持foreach循环、生成器(认为是yield),并且由于它的接口很小,所以用于创建紧密的抽象。IEnumerable依赖于IEnumerator。

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

    可枚举的

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

    前循环

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

    我认为foreach循环是使用IEnumerable接口的主要原因之一。与经典的循环C样式相比,foreach具有非常简洁的语法和非常容易理解的特性,在循环中,您需要检查各种变量以了解它在做什么。

    屈服关键字

    可能还有一个鲜为人知的特性是,IEnumerable还可以使用yield returnyield break语句在C中启用生成器。

    1
    2
    3
    4
    5
    IEnumerable<Thing> GetThings() {
       if (isNotReady) yield break;
       while (thereIsMore)
         yield return GetOneMoreThing();
    }

    抽象化

    实践中的另一个常见场景是使用IEnumerable来提供最低限度的抽象。因为它是一个微小的只读接口,所以鼓励您将集合公开为IEnumerable(而不是List)。这样,您就可以在不破坏客户端代码的情况下自由地更改实现(例如,将列表更改为LinkedList)。

    果茶

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

    迭代器

    另一方面,IEnumerator是一个幕后接口,它使IEnumerable在每个魔术中都起作用。严格来说,它支持显式迭代器。

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

    根据我的经验,IEnumerator很少用于常见的场景中,因为它的语法更加冗长,语义也有点混乱(至少对我来说,例如moveNext()也会返回一个值,而这个值的名称一点也不建议)。

    IEnumerator的用例

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

    客户端代码

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

    图书馆

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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;  
        }
    }


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

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

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


    IEnumerable和IEnumerator之间的区别:

    • IEnumerable在内部使用IEnumerator。
    • IEnumerable不知道正在执行哪个项/对象。
    • 每当我们将IEnumerator传递给另一个函数时,它就知道项/对象的当前位置。
    • 每当我们将IEnumerable集合传递给另一个函数时,不知道项/对象的当前位置(不知道正在执行哪个项)

      IEnumerable有一个方法getEnumerator()。

    1
    2
    3
    4
    public interface IEnumerable<out T> : IEnumerable
    {
    IEnumerator<T> GetEnumerator();
    }

    IEnumerator有一个属性current和两个方法reset和movenext(这对于了解列表中项目的当前位置很有用)。

    1
    2
    3
    4
    5
    6
    public interface IEnumerator
    {
         object Current { get; }
         bool MoveNext();
         void Reset();
    }

    了解迭代器模式将对您有所帮助。我建议你读同样的书。

    迭代器模式

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

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

    这是UML图Iterator Pattern

    所以基本上在C中,IEnumerable是抽象聚合,IEnumerator是抽象迭代器。IEnumerable有一个方法getEnumerator,该方法负责创建所需类型的IEnumerator实例。像列表这样的集合实现IEnumerable。

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

    为了计算排列的数目,我们可以做如下的事情。

    1
    2
    3
    4
    5
    6
     int count = 0;
            var permutations = perm.getPermutations(inputString);
            foreach (string permutation in permutations)
            {
                count++;
            }

    C编译器或多或少地将上述内容转换为

    1
    2
    3
    4
    5
    6
    7
    using (var permutationIterator = perm.getPermutations(input).GetEnumerator())
            {
                while (permutationIterator.MoveNext())
                {
                    count++;
                }
            }

    如果您有任何问题,请不要犹豫。


    我注意到这些差异:

    a.我们以不同的方式迭代列表,foreach可用于IEnumerable,while循环可用于IEnumerator。

    b.当我们从一个方法传递到另一个方法时,IEnumerator可以记住当前索引(它开始处理当前索引),但IEnumerable无法记住该索引并将索引重置为开始。视频中有更多内容https://www.youtube.com/watch?V= JD3YYJGC9M0


    次要贡献。

    他们中的许多人解释了"何时使用"和"与foreach一起使用"。我考虑在这里添加另一个状态差异,这是关于IEnumerable和IEnumerator之间的差异的问题所要求的。

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

    IEnumerable、IEnumerator与ForEach,何时使用IEnumerator和IEnumerable有什么区别?

    枚举器保留函数调用之间的状态(迭代位置),而另一方可枚举的迭代则不保留。

    这是一个测试示例,其中包含要理解的注释。

    请专家给我加/改。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    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的框。IEnumerable是所有集合的基接口。如果集合实现IEnumerable,则foreach循环可以操作。在下面的代码中,它解释了拥有自己的枚举器的步骤。让我们先定义我们要创建集合的类。

    1
    2
    3
    4
    5
    6
    7
    public class Customer
    {
        public String Name { get; set; }
        public String City { get; set; }
        public long Mobile { get; set; }
        public double Amount { get; set; }
    }

    现在我们将定义类,它将作为类客户的集合。请注意,它正在实现接口IEnumerable。所以我们必须实现getEnumerator方法。这将返回我们的自定义枚举器。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     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循环遍历集合,如下所示;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
        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();

            }
        }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    namespace Enudemo
    {

        class Person
        {
            string name ="";
            int roll;

            public Person(string name, int roll)
            {
                this.name = name;
                this.roll = roll;
            }

            public override string ToString()
            {
                return string.Format("Name :" + name +"\t Roll :" + roll);
            }

        }


        class Demo : IEnumerable
        {
            ArrayList list1 = new ArrayList();

            public Demo()
            {
                list1.Add(new Person("Shahriar", 332));
                list1.Add(new Person("Sujon", 333));
                list1.Add(new Person("Sumona", 334));
                list1.Add(new Person("Shakil", 335));
                list1.Add(new Person("Shruti", 336));
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
               return list1.GetEnumerator();
            }
        }



        class Program
        {
            static void Main(string[] args)
            {
                Demo d = new Demo();  // Notice here. it is simple object but for
                                    //IEnumerator you can get the collection data

                foreach (Person X in d)
                {
                    Console.WriteLine(X);
                }

                Console.ReadKey();
            }
        }
    }
    /*
    Output :

    Name : Shahriar  Roll : 332
    Name : Sujon     Roll : 333
    Name : Sumona    Roll : 334
    Name : Shakil    Roll : 335
    Name : Shruti    Roll : 336
      */

    IEnumerableIEnumerator都是C中的接口。

    IEnumerable是定义返回IEnumerator接口的单个方法GetEnumerator()的接口。

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

    IEnumerator有两种方法,MoveNextReset。它还有一个叫Current的财产。

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