IEnumerable,IEnumerator vs foreach,何时使用什么

时间:2009-07-06 06:14:00

标签: c# enumeration

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

3 个答案:

答案 0 :(得分:49)

在许多情况下,

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>视为表格,{ {1}}作为游标。你可以要求一个表给你一个新的光标,你可以同时在同一个表上有多个游标。

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

答案 1 :(得分:8)

Jon说的话。

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

答案 2 :(得分:6)

请记住,你没有 实现IEnumerator及其变量以使用foreach - IIRC,它只需要一个GetEnumerator()方法返回一个具有MoveNext()的对象返回bool的方法,以及返回对象的Current属性。您不需要使用IEnumerator和IEnumerable,尽管这样做通常是一个非常好的主意。有关详细信息,请参阅here