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

例如,什么时候用它胜过foreach?IEnumerable和IEnumerator的区别是什么?为什么我们需要使用它?


当前回答

理解Iterator模式将对您有所帮助。我建议你也读一下。

迭代器模式

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

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

下面是UML图

在c#中,IEnumerable是抽象的集合IEnumerator是抽象的迭代器。IEnumerable有一个单独的方法GetEnumerator,它负责创建所需类型的IEnumerator实例。像list这样的集合实现了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和IEnumerator被透明地用作foreach循环的一部分。

小贡献。

正如他们中的许多人解释“何时使用”和“与foreach一起使用”。 我想在这里添加另一个状态差异,以询问IEnumerable和IEnumerator之间的差异。

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

IEnumerable, IEnumerator vs foreach,什么时候使用什么 IEnumerator和IEnumerable的区别是什么?

Enumerator保留函数调用之间的状态(迭代位置),而Enumerable则不保留迭代。

下面是带有注释的测试示例。

专家请补充/纠正我。

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都是c#中的接口。

IEnumerable是一个接口,它定义了一个返回IEnumerator接口的方法GetEnumerator()。

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

IEnumerator有两个方法,MoveNext和Reset。它还有一个名为Current的属性。

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

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

IEnumerable和IEnumerator(以及它们的泛型对应物IEnumerable<T>和IEnumerator<T>)是. net Framework类库集合中迭代器实现的基本接口。

IEnumerable是大多数代码中最常见的接口。它支持foreach循环,生成器(考虑yield),由于它的小接口,它被用于创建紧密的抽象。IEnumerable依赖于IEnumerator。

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

IEnumerable

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

foreach循环

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

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

生成的关键字

可能一个不太为人所知的特性是IEnumerable还通过使用yield return和yield break语句支持c#中的生成器。

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

抽象

实践中的另一个常见场景是使用IEnumerable来提供极简抽象。因为它是一个很小的只读接口,所以建议将集合公开为IEnumerable(而不是List)。这样你就可以在不破坏客户端代码的情况下自由更改实现(例如将List更改为LinkedList)。

问题

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

分子

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

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

根据我的经验,IEnumerator很少在常见场景中使用,因为它的语法比较冗长,语义有点混乱(至少对我来说是这样;例如,MoveNext()也返回一个值,这个名称根本不建议)。

IEnumerator的用例

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