任何人都可以解释的类型和IEnumerator到我吗?

例如,当使用它foreach?之间的区别是什么类型和IEnumerator?为什么我们需要使用它?

有帮助吗?

解决方案

  

例如,当过度的foreach使用它?

您不要使用IEnumerable “过度” foreach。实施IEnumerable使用foreach使得可能

当你写类似的代码:

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

它的功能上等同于写作:

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

这是“相同的功能,”我的意思是这实际上是什么编译器开启的代码之中。你不能在这个例子上foreach使用baz除非baz实现IEnumerable

IEnumerable意味着baz实现该方法

IEnumerator GetEnumerator()

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

bool MoveNext()

Object Current()

如果它这样做的第一个方法前进到在创建该枚举,返回IEnumerablefalse对象的下一个对象,并且所述第二返回当前对象。

任何NET中,你可以遍历器具IEnumerable。如果您正在构建自己的类,它不从已经实现IEnumerable一个类继承,您可以通过实现foreach(通过创建一个枚举类,其新IEnumerable方法将返回),让你的类GetEnumerator报表可用

其他提示

<强>的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进行评价construct.To 举例说明,跟我走!

假设我们有一个车库类:

// 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在车库对象的子项来迭代 构造,就像数据值的数组:

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

不幸的是,编译器会通知您的车库类没有实现一个命名的方法 的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.
}

如果您要更新的车库类型以支持这些接口,你可以走漫长的道路和 手动实现每个方法。虽然你肯定免费提供的定制版本 的GetEnumerator()操作,MoveNext(),当前和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();
   }
}

您已经更新您的车库类型后,您可以安全地使用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();
}

通过这样做,不经意对象用户将无法找到车库的GetEnumerator()方法,而 foreach结构将获得背景的接口在必要时。

这适于在临C#5.0和.NET 4.5框架

实施的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...
}

这就是区别反正。

类比解释+代码演练

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

假设您正在经营一家航空公司。在每架飞机上,您都想了解机上乘客的信息。基本上你希望能够“穿越”飞机。换句话说,您希望能够从前排座位开始,然后向飞机后部询问乘客一些信息:他们是谁,来自哪里等。飞机只能做到这一点,如果它是:

  1. 可数,并且
  2. 如果它有一个计数器。

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

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

可数是什么意思?

如果一家航空公司是“可数的”,这意味着飞机上必须有一名空姐,其唯一的工作就是数数 - 并且该空姐必须以非常具体的方式进行数数:

  1. 柜台/空乘人员必须在第一位乘客之前开始(在每个人的前面,他们演示安全性,如何穿救生衣等)。
  2. 他/她(即空乘人员)必须沿着过道“移动到下一个”到第一个座位。
  3. 然后他/她将记录:(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) 并了解 当前的 与他打交道的人。

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

IEnumerable的实现的GetEnumerator。调用时,该方法会返回一个 IEnumerator的它实现的MoveNext,复位和电流。

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

实施了IEnumerable使您能够获得一个IEnumerator的列表。

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

的foreach实现(在Java 1.4中,例如)之前,遍历一个列表的方式是从列表中得到一个枚举,然后问它在列表中的“下一个”项目,只要返回的值作为下一个项目是不为空。的foreach来说确实隐含这样的语言特性,在锁定(以同样的方式)实现了后台监控类。

我期待的foreach作品名单上,因为他们实现IEnumerable

  • 一个对象实现 IE可枚举 允许其他人访问其每个项目 (由普查员提供).
  • 一个对象实现 IE枚举器 是进行迭代。它循环遍历一个可枚举对象。

将可枚举对象视为列表、堆栈、树。

类型和IEnumerator(和他们的通用同行的类型<T> 和IEnumerator<T>)是基口的 迭代 实现在 .净框架类不能直接集合.

类型 是最常见的接口,你会看看大多数的代码。它使foreach循环发电机(觉得 产量)和由于其微小的接口,它用来建立紧密的抽象概念。 类型取决于IEnumerator.

IEnumerator, 另一方面,提供了一个稍微低级别的迭代的接口。这是被称为 明确的迭代 这给程序的更多控制的迭代循环。

类型

类型是一个标准的界面,使循环的集合,支持它(事实上,所收集类型我可以认为今天实现了 类型).编译器支持允许的语言功能喜欢 foreach.在一般条款中,它使这 隐含的迭代执行情况.

foreach循环

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

我认为 foreach 循环是一个主要原因的使用 类型 接口。 foreach 有一个非常简洁的语法并非常容易理解比较经典 C 风格的循环,你需要检查的各种变量来看看它是什么做的。

产率的关键字

可能是一个鲜为人知的特点是, 类型 还使 发电机在C# 与使用 yield returnyield break 发言。

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

抽象

另一个常见的情况在实践中被使用 类型 提供简单的抽象概念。因为这是一个微不足道,只读界面,我们鼓励你让你的收藏 类型 (而不是 列表 例如)。这样你可以自由改变你的实施不会破坏你的客户的代码(更改列到一个 链表 例如)。

疑难杂症

一个行为要注意的是,在流实现(例如检索的数据逐行从一个数据库,而不是载的所有结果在存储器的一)你 不能 迭代过收集更多的一次。这是相对于在内存收藏似的 列表, ,哪里可以重多次没有问题。ReSharper,例如, 有一个代码检查 尽可能多枚举的类型.

IEnumerator

IEnumerator,另一方面,是幕后的接口,这使得 IEnumerble-foreach-魔术 的工作。严格来说,它能够明确迭代器。

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

以我的经验 IEnumerator 很少使用常见的情况下,由于其更详细的语法和略语义混淆的(至少对我来说;例如 MoveNext() 返回价值以及它的名称不建议在所有)。

使用的情况下为IEnumerator

我只用 IEnumerator 特别是(略低一级)库和框架,我在那里提供 类型 接口。一个实例是数据流处理图书馆提供了一系列的对象 foreach 环即使在幕后的数据收集使用各种文件流和serialisations.

客户代码

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

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

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

大多数时候,IEnumerableIEnumerator的被透明地用作foreach回路的一部分。

IEnumerable 和 IEnumerator 之间的区别:

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

    IEnumerable 有一种方法 GetEnumerator()

public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}

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

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

迭代器模式的理解将是对你有帮助。我建议读取相同。

Iterator模式

在高级别上的迭代器图案可以用来提供通过任何类型的集合迭代的标准方式。 我们在迭代器模式3名参与者,实际收集(客户端),聚合器和迭代器。聚集体是具有一个返回一个迭代的方法的接口/抽象类。迭代器是具有允许我们迭代通过集合的方法的接口/抽象类。

为了实现我们首先需要实现一个迭代以产生具体的是可以遍历有关集合图案(客户端) 然后收集(客户端)实现聚合器返回上述迭代器的一个实例。

下面是UML图 “迭代器模式”

所以在c#基本上,IEnumerable的是抽象骨料和IEnumerator的是抽象迭代器。 IEnumerable的具有单一方法GetEnumerator即负责创建所需类型的IEnumerator的的一个实例。像列出了集合实现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循环可以操作。在下面的代码它说明有我们自己的枚举的步骤。让我们先定义我们的类的,我们要进行收集。

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。这将返回我们的自定义枚举。

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

        }
    }

一个次要贡献。

,因为其中许多关于“何时使用”和“用foreach使用”解释。 我想增加另外的国家差异这里的要求,在有关的问题既IEnumerable的一个IEnumerator。之间的区别

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

的IEnumerable,IEnumerator的VS的foreach,当使用什么 是什么的IEnumerator和IEnumerable?

枚举保留状态(迭代位置)之间的函数调用而迭代另一方面可枚举没有。

下面是所测试的示例带注释理解。

专家请加/指正。

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

我注意到这些差异:

一个。我们迭代在不同方式列表中,的foreach可用于IEnumerable和while循环为IEnumerator的。

B中。 IEnumerator的能记住当前索引,当我们从一个方法传递到另一个(它开始与当前索引的工作),但IEnumerable的不记得指数和它的索引重置为开头。更多在这个视频 https://www.youtube.com/watch?v=jd3yUjGc9M0

IEnumerableIEnumerator 两者都是 C# 中的接口。

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

这适用于对实现该功能的集合的只读访问 IEnumerable 可以与 foreach 陈述。

IEnumerator 有两种方法, MoveNextReset. 。它还有一个属性叫做 Current.

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

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
  */
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top