有人能给我解释一下IEnumerable和IEnumerator吗?
例如,什么时候用它胜过foreach?IEnumerable和IEnumerator的区别是什么?为什么我们需要使用它?
有人能给我解释一下IEnumerable和IEnumerator吗?
例如,什么时候用它胜过foreach?IEnumerable和IEnumerator的区别是什么?为什么我们需要使用它?
当前回答
通过类比+代码演练进行解释
类比:假设你是一架飞机上的侦探。你得在所有乘客中找到嫌疑人。
一架飞机只能这样做,如果它是:
可数名词, 如果它有计数器。
可数是什么意思?
如果一家航空公司是可数的,这意味着飞机上必须有一名空乘人员,他的唯一工作就是数数:
柜台/空乘人员必须先于第一位乘客开始工作。(即空乘人员)必须“下一个移动”到过道的第一个座位。 然后他/她要记录:(i)坐在座位上的人,以及(ii)他们目前在过道上的位置。
计数器一直往前走,直到他到达飞机的尽头。
让我们把它和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)了解他正在处理的当前对象。
可枚举的只是“可数的”的一个花哨的词。
其他回答
例如,什么时候用它胜过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。
IEnumerator允许使用yield关键字以foreach样式顺序访问列表中的项。
在foreach实现之前(例如在Java 1.4中),迭代列表的方法是从列表中获取一个枚举器,然后向它请求列表中的“下一个”项,只要作为下一个项返回的值不为空。Foreach只是隐式地将其作为一种语言特性来实现,与lock()在幕后实现Monitor类的方式相同。
我期望foreach工作在列表上,因为它们实现了IEnumerable。
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
*/
通过类比+代码演练进行解释
类比:假设你是一架飞机上的侦探。你得在所有乘客中找到嫌疑人。
一架飞机只能这样做,如果它是:
可数名词, 如果它有计数器。
可数是什么意思?
如果一家航空公司是可数的,这意味着飞机上必须有一名空乘人员,他的唯一工作就是数数:
柜台/空乘人员必须先于第一位乘客开始工作。(即空乘人员)必须“下一个移动”到过道的第一个座位。 然后他/她要记录:(i)坐在座位上的人,以及(ii)他们目前在过道上的位置。
计数器一直往前走,直到他到达飞机的尽头。
让我们把它和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)了解他正在处理的当前对象。
可枚举的只是“可数的”的一个花哨的词。
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框架