为什么我们需要两个接口来枚举集合?

Sip*_*ipo 7 c# ienumerable ienumerator foreach

这是相当长一段时间,我一直在试图了解背后的想法IEnumerableIEnumerator.我阅读了网上可以找到的所有问题和答案,特别是StackOverflow,但我并不满意.我明白我应该如何使用这些接口,但不是为什么这样使用它们.

我认为我误解的本质是我们需要两个接口进行一次操作.我意识到,如果两者都需要,可能还不够.所以我采用了"硬编码"等价物foreach(正如我在这里找到的):

while (enumerator.MoveNext())
{
    object item = enumerator.Current;

    // logic
}
Run Code Online (Sandbox Code Playgroud)

并试图让它与一个界面一起工作,认为某些事情会出错,这将使我理解为什么需要另一个界面.

所以我创建了一个集合类,并实现了IForeachable:

class Collection : IForeachable
{
    private int[] array = { 1, 2, 3, 4, 5 };
    private int index = -1;

    public int Current => array[index];

    public bool MoveNext()
    {
        if (index < array.Length - 1)
        {
            index++;
            return true;
        }

        index = -1;
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用foreach等价物来提名集合:

var collection = new Collection();

while (collection.MoveNext())
{
    object item = collection.Current;

    Console.WriteLine(item);
}
Run Code Online (Sandbox Code Playgroud)

它的工作原理!那么这里缺少什么需要另一个接口?

谢谢.


编辑: 我的问题与评论中列出的问题不重复:

  • 这个问题就是为什么首先需要枚举接口的原因.
  • 这个问题和这个问题是关于这些接口是什么以及它们应该如何使用.

我的问题是为什么它们按照它们的方式设计,而不是它们是什么,它们如何工作,以及为什么我们首先需要它们.

Col*_*kay 5

这两个接口是什么,它们做什么?

IEnumerable接口放在集合对象上并定义GetEnumerator()方法,它返回一个实现IEnumerator接口的(通常是新的)对象.C#中的foreach语句和VB.NET中的For Each语句使用IEnumerable访问枚举器以循环遍历集合中的元素.

IEnumerator接口实际上是放在实际进行迭代的对象上的契约.它存储迭代的状态,并在代码在集合中移动时更新它.

为什么不将集合也作为枚举器呢?为什么有两个独立的接口?

没有什么可以阻止IEnumerator和IEnumerable在同一个类上实现.但是,执行此操作会受到惩罚 - 同一个集合上不可能有两个或更多个循环.如果可以绝对保证不需要同时在集合上循环两次那么那就没问题了.但在大多数情况下是不可能的.

什么时候有人会一次多次迭代一个集合?

这是两个例子.

第一个例子是在同一个集合中有两个嵌套在彼此内部的循环.如果集合也是枚举器,则不可能在同一集合上支持嵌套循环,当代码到达内部循环时,它将与外部循环冲突.

第二个示例是当有两个或更多线程访问同一个集合时.同样,如果集合也是枚举器,那么就不可能在同一集合上支持安全的多线程迭代.当第二个线程尝试遍历集合中的元素时,两个枚举的状态将发生冲突.

此外,由于.NET中使用的迭代模型不允许在枚举期间对集合进行更改,因此这些操作完全是安全的.

- 这是我多年前写的一篇博文:https://colinmackay.scot/2007/06/24/iteration-in-net-with-ienumerable-and-ienumerator/