当前位置:  开发笔记 > 编程语言 > 正文

任何人都可以向我解释IEnumerable和IEnumerator吗?

如何解决《任何人都可以向我解释IEnumerable和IEnumerator吗?》经验,为你挑选了10个好方法。

任何人都可以向我解释IEnumerable和IEnumerator吗?

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



1> Robert Rossn..:

例如,什么时候在foreach上使用它?

你不要使用IEnumerable"over" foreach.实施IEnumerable使得使用成为foreach 可能.

当您编写如下代码时:

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

它在功能上等同于写作:

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

通过"功能相同",我的意思是编译器实际上将代码转换为什么.除非实现,否则在此示例中不能使用foreachon . baz bazIEnumerable

IEnumerable意味着baz实现该方法

IEnumerator GetEnumerator()

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

bool MoveNext()

Object Current()

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

.Net中你可以迭代实现的任何东西IEnumerable.如果您正在构建自己的类,并且它尚未从实现的类继承IEnumerable,则可以foreach通过实现IEnumerable(以及通过创建其新GetEnumerator方法将返回的枚举器类)使您的类在语句中可用.


我认为当原始海报说"超过foreach"时,他的意思是"我何时应该明确地调用GetEnumerator()/ MoveNext()而不是使用foreach循环." 物有所值.
这个答案并不能说明整个故事.可枚举对象无需实现IEnumerable; 它们只需要一个GetEnumerator方法,该方法返回一个类型的实例,该类型又具有`bool MoveNext()`方法和任何类型的`Current`属性.这种"鸭子打字"方法在C#1.0中实现,作为在枚举值类型集合时避免装箱的一种方法.
啊,我认为你是对的.好吧,答案隐含在我的帖子中:它没关系,因为这就是编译器所做的事情.所以做最简单的事情,即使用foreach.
这有点像C++`iterator`吗?
通过上面的exlanation并通过[Workthrough:Implementing IEnumerable of(T)](http://msdn.microsoft.com/en-us/library/vstudio/dd145423.aspx),您可以获得更多资源.

2> Jahan..:

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接口形式化,该接口位于System.Collections命名空间中.支持此行为的类或结构通告它们能够将包含的子项公开给调用者(在此示例中,为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()的自定义版本,但有一种更简单的方法.由于System.Array类型(以及许多其他集合类)已经实现了IEnumerable和IEnumerator,您可以简单地将请求委托给System.Array,如下所示:

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();
   }
}

更新Garage类型后,可以安全地使用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 Framework



3> core..:

实现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...
}

无论如何,这是不同的.



4> BKSpurgeon..:

通过类比+代码演练解释

首先是没有代码的解释,然后我会在以后添加它.

假设您正在经营一家航空公司.在每架飞机上,您都想知道飞机上乘客的信息.基本上你希望能够"遍历"飞机.换句话说,你希望能够从前座开始,然后沿着飞机后方前进,向乘客询问一些信息:他们是谁,他们来自哪里等.飞机只能这样做, 如果是:

    可数的,和

    如果它有一个柜台.

为什么这些要求?因为这就是界面所需要的.

如果这是信息过载,您需要知道的是,您希望能够向飞机上的每位乘客询问一些问题,从第一个开始,直到最后一个.

可数意味着什么?

如果航空公司是"可数"的,这意味着飞机上必须有一名乘务员,他的唯一工作就是计算 - 而这名乘务员必须以非常具体的方式计算:

    柜台/空乘人员必须在第一位乘客之前开始(在他们演示安全的每个人的前面,如何把救生衣放在等等).

    他/她(即空乘人员)必须"沿着过道向下移动"到第一个座位.

    然后他/她将记录:(i)该人在座位上的人,以及(ii)他们目前在过道中的位置.

计数程序

航空公司的船长希望在每位乘客被调查或计算时对其进行报告.因此,在与第一个座位上的人员交谈后,乘务员/柜台然后向船长报告,当报告发出时,计数器会记住他/她在过道中的确切位置并继续计算他/她离开的位置关闭.

通过这种方式,船长始终能够获得有关当前被调查人员的信息.这样,如果他发现这个人喜欢曼城,那么他可以给予该乘客优惠待遇等.

计数器一直持续到飞机末端.

让我们将其与IEnumerables联系起来

可枚举的只是飞机上的乘客集合.民航法 - 这些基本上是所有IEnumerables必须遵循的规则.每当航空公司服务员带着乘客信息前往船长时,我们基本上都会"让"乘客到船长.机长基本上可以随意为乘客做任何事情 - 除了重新安排飞机上的乘客.在这种情况下,如果他们跟随曼城,他们会得到优惠待遇(呃!)

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)并且知道他正在处理的当前人.

可枚举只是"可数"的一个奇特的词.换句话说,可枚举允许您"枚举"(即计数).



5> DavGarcia..:

IEnumerable实现了GetEnumerator.调用时,该方法将返回一个实现MoveNext,Reset和Current 的IEnumerator.

因此,当你的类实现IEnumerable时,你说你可以调用一个方法(GetEnumerator)并获得一个新的对象返回(一个IEnumerator),你可以在一个循环中使用它,比如foreach.



6> Neil Barnwel..:

实现IEnumerable使您可以获取列表的IEnumerator.

IEnumerator允许使用yield关键字对列表中的项进行foreach样式顺序访问.

在foreach实现之前(例如在Java 1.4中),迭代列表的方法是从列表中获取枚举数,然后向列表中的"next"项询问,只要该值返回为下一个item不为null.Foreach只是隐式地将其作为语言特征,就像lock()在幕后实现Monitor类一样.

我希望foreach可以在列表上工作,因为它们实现了IEnumerable.



7> Lieven Keers..:

实现IEnumerable的对象允许其他人访问其每个项目(通过枚举器).

实现IEnumerator的对象是进行迭代.它循环遍历一个可枚举的对象.

考虑列表,堆栈,树中的可枚举对象.



8> ziya..:

IEnumerable和IEnumerator(及其通用对应物IEnumerable 和IEnumerator )是.Net Framework类Libray集合中迭代器实现的基接口.

IEnumerable是你在大多数代码中看到的最常见的接口.它支持foreach循环,生成器(想想产量),并且由于其微小的界面,它用于创建紧密的抽象.IEnumerable依赖于IEnumerator.

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

IEnumerable的

IEnumerable是一个标准接口,可以迭代支持它的集合(实际上,我今天能想到的所有集合类型都实现了IEnumerable).编译器支持允许语言功能foreach.一般而言,它启用此隐式迭代器实现.

foreach Loop

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

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

产量关键字

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

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

抽象

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

疑难杂症

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

IEnumerator的

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

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

根据我的经验,IEnumerator在常见场景中很少使用,因为它的语法更加冗长,语义略有混乱(至少对我而言;例如,MoveNext()也返回一个值,该名称根本没有提示).

IEnumerator的用例

我只使用IEnumerator特别(略低级别)的库和框架,我提供IEnumerable接口.一个示例是数据流处理库,其在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;   
    }
}



9> Dan Herbert..:

实现IEnumerable本质上意味着可以迭代对象.这并不一定意味着它是一个数组,因为某些列表无法编入索引,但您可以枚举它们.

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

大多数时候,IEnumerable&IEnumerator作为foreach循环的一部分透明地使用.



10> Ajay..:

IEnumerable和IEnumerator之间的差异:

IEnumerable在内部使用IEnumerator.

IEnumerable不知道正在执行哪个项目/对象.

每当我们将IEnumerator传递给另一个函数时,它就知道item/object的当前位置.

每当我们将IEnumerable集合传递给另一个函数时,它就不知道item/object的当前位置(不知道它正在执行哪个项目)

IEnumerable有一个方法GetEnumerator()

public interface IEnumerable : IEnumerable
{
    IEnumerator GetEnumerator();
}

IEnumerator有一个Property current和两个方法Reset和MoveNext(这对于了解列表中项目的当前位置很有用).

public interface IEnumerator
{
     object Current { get; }
     bool MoveNext();
     void Reset();
}

推荐阅读
贾志军
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有