26

我正在通过 IEnumerable 和 IEnumerator ,但无法清楚地得到一点..如果我们有 foreach,那么为什么我们需要这两个接口?有没有我们必须使用接口的场景。如果是,那么有人可以举个例子来解释一下。欢迎任何建议和评论。谢谢。

4

3 回答 3

57

foreach 在许多情况下使用接口。如果要实现可以使用的序列,foreach则需要接口。(迭代器块通常使这个实现任务非常简单。)

但是,有时直接使用迭代器会很有用。一个很好的例子是尝试“配对”两个不同的序列。例如,假设您收到两个序列 - 一个是姓名,一个是年龄,并且您希望将这两个序列一起打印。你可能会写:

static void PrintNamesAndAges(IEnumerable<string> names, IEnumerable<int> ages)
{
    using (IEnumerator<int> ageIterator = ages.GetEnumerator())
    {
        foreach (string name in names)
        {
            if (!ageIterator.MoveNext())
            {
                throw new ArgumentException("Not enough ages");
            }
            Console.WriteLine("{0} is {1} years old", name, ageIterator.Current);
        }
        if (ageIterator.MoveNext())
        {
            throw new ArgumentException("Not enough names");
        }

    }
}

同样,如果您想以与其他项目不同的方式处理(例如)第一项,则使用迭代器会很有用:

public T Max<T>(IEnumerable<T> items)
{
    Comparer<T> comparer = Comparer<T>.Default;

    using (IEnumerator<T> iterator = items.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            throw new InvalidOperationException("No elements");
        }
        T currentMax = iterator.Current;

        // Now we've got an initial value, loop over the rest
        while (iterator.MoveNext())
        {
            T candidate = iterator.Current;
            if (comparer.Compare(candidate, currentMax) > 0)
            {
                currentMax = candidate;
            }
        }
        return currentMax;
    }
}

现在,如果您对 和 之间的区别感兴趣IEnumerator<T>IEnumerable<T>您可能想用数据库术语来考虑它:将其IEnumerable<T>视为表和IEnumerator<T>游标。你可以要求一个表给你一个新的游标,你可以在同一个表上同时有多个游标。

真正理解这种差异可能需要一段时间,但只要记住一个列表(或数组,或其他)没有任何“你在列表中的位置”的概念,而是对该列表/数组/无论什么的迭代器有那种状态是有帮助的。

于 2009-07-06T06:15:46.080 回答
9

乔恩说的。

  • IEnumerableIEnumerable<T>:通过实现这个对象声明它可以给你一个迭代器,你可以用它来遍历序列/集合/集合
  • IEnumeratorIEnumerator<T>: 如果调用之前接口中定义的 GetEnumerator 方法,则会得到一个迭代器对象作为 IEnumerator 引用。这使您可以调用 MoveNext() 并获取 Current 对象。
  • foreach: 从某种意义上说,它是一个 C# 构造/外观,因为您不需要知道它在幕后是如何工作的。它在内部获取迭代器并调用正确的方法让您专注于您想要对每个项目执行的操作(foreach 块的内容)。大多数时候,您只需要 foreach ,除非您正在实现自己的类型或自定义迭代,在这种情况下,您需要了解前 2 个接口。
于 2009-07-06T06:54:26.910 回答
7

请记住,您不必实现IEnumerator 及其变体即可使用 foreach - IIRC,它所需要的只是一个 GetEnumerator() 方法,该方法返回一个对象,该对象具有返回 bool 的 MoveNext() 方法和返回的 Current 属性一个东西。您不需要使用 IEnumerator 和 IEnumerable,尽管这样做通常是一个非常好的主意。请参阅此处了解更多信息。

于 2009-07-06T09:47:44.577 回答