理解IEnumerator有困难

本文关键字:IEnumerator 理解 | 更新日期: 2023-09-27 18:13:32

我目前正在学习c#,目的是有效地使用ingrazor和Umbraco,我有一点PHP背景和JavaScript的合理知识。

我遇到了IEnumerator接口,并且很难理解它。我在下面设置了一个代码片段来演示使用foreach:

循环遍历数组
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace cardEnum
{
    class suitsEnumerator
    {
        private static string[] suits = { "Hearts", "Spades", "Diamonds", "Clubs" };
        public void showAll()
        {
            foreach(string x in suits){
                System.Console.WriteLine(x);
            }
        }
    }
    class run
    {
        static void Main()
        {
            Console.WriteLine("The suits are:");
            var suits = new suitsEnumerator();
            suits.showAll();
            Console.ReadKey();
        }
    }
}

有人可以解释为什么我甚至需要使用IEnumerator时,我可以很容易地通过数组循环。我知道这是一个如此简单的问题,我只是想不出使用IEnumerable的理由。

任何帮助都将非常感激,谢谢!

理解IEnumerator有困难

这就是枚举器模式在您的示例中的实现方式。我希望这能澄清一些事情,特别是IEnumerableIEnumerator之间的关系:

namespace cardEnum {
    // simple manual enumerator
    class SuitsEnumerator : IEnumerator {
        private int pos = -1;
        private static string[] suits = { "Hearts", "Spades", "Diamonds", "Clubs" };
        public object Current {
            get { return suits[pos]; }
        }
        public bool MoveNext() {
            pos++;
            return (pos < suits.Length);
        }
        public void Reset() {
            pos = -1;
        }
    }
    class Suits : IEnumerable {
        public IEnumerator GetEnumerator() {
            return new SuitsEnumerator();
        }
    }
    class run {
        static void Main() {
            Console.WriteLine("The suits are:");
            var suits = new Suits();
            foreach (var suit in suits) {
                Console.WriteLine(suit);
            }
            Console.ReadKey();
        }
    }
}

当然,这是一个人为的例子来帮助理解。既然string[]已经实现了IEnumerable,就没有理由重新发明轮子了。


由于c#支持yields关键字,最近创建枚举器变得简单了一些:

namespace cardEnum {
    // no explicit SuitsEnumerator required
    class Suits : IEnumerable {
        public IEnumerator GetEnumerator() {
            yield return "Hearts";
            yield return "Spades";
            yield return "Diamonds";
            yield return "Clubs";
        }
    }
    class run {
        static void Main() {
            Console.WriteLine("The suits are:");
            var suits = new Suits();
            foreach (var suit in suits) {
                Console.WriteLine(suit);
            }
            Console.ReadKey();
        }
    }
}

首先,如果你要创建你自己的枚举器,你应该让类实现IEnumerator接口(或它的泛型等价)。

public class FooEnumerator : IEnumerator
{
    #region IEnumerator Members
    public object Current
    {
        get { throw new NotImplementedException(); }
    }
    public bool MoveNext()
    {
        throw new NotImplementedException();
    }
    public void Reset()
    {
        throw new NotImplementedException();
    }
    #endregion
}

枚举数通常用于在集合中每次移动一个项。这是一个简单的接口,用于支持移动的项目,但不表示项目本身。它只有当前项目和移动到下一个项目的概念。

另一方面,

IEnumerable用于表示项的集合。IEnumerable根据其定义返回一个枚举数,其目的是枚举IEnumerable集合中的每个项。它们不是互斥类,它们是依赖类。

您通常不会像在示例中那样创建枚举器来遍历您的值。您将创建一个集合并使用枚举数来导航它(或者在更高的级别上迭代在幕后使用枚举数的集合)。

除了打印对象的值之外,您可能(而且经常)需要执行一些更复杂的操作。

你的类可能不得不一个接一个地返回(生成)对象以进行一些外部处理(由其他类、方法等)。

那么暴露IEnumerator比返回整个集合要好,因为枚举意味着一次只传递一个对象!

不是一个同时包含(可能)一百万个元素的数组。

c#编译器将此转换为

foreach(string x in suits)

使用IEnumerator(或IEnumerator<T>)。

IEnumerableIEnumerable<T>接口定义了一个GetEnumerator方法,该方法返回一个IEnumerator(或分别为IEnumerator<T>),这是您正在使用foreach进行迭代所使用的。

例外是对数组进行迭代,编译器对数组的处理方式不同(感谢@Jon Skeet)。

这很好,因为你的代码依赖于接口(IEnumerable)

今天的西服类型在你的代码是Array

private static string[] suits = { "Hearts", "Spades", "Diamonds", "Clubs" };

如果你决定使用for循环,你的代码看起来或多或少像下面的

for (int index = 0; index < suits.Length; suits++)
{
    // code here
}

明年您可能有理由将其类型从Array更改为List<T>。如果你使用for,你需要更新代码块,因为数组有Length,而列表有Count属性。

如果你看看System。你会看到Array实现了IEnumerable。每个都不需要IEnumerable对象。它只需要一个名为GetEnumerator的方法

假设您要创建一个自定义套装集合…学习如何实现IEnumerable<T>将给你一点洞察为什么它是如此有用。没有它,LINQ就不可能存在。