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

Won*_*ing 23 c# enumeration

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

Jon*_*eet 50

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

    }
}
Run Code Online (Sandbox Code Playgroud)

同样,如果你想要(比方说)第一个项目与其他项目不同,那么使用迭代器会很有用:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果您对IEnumerator<T>和之间的区别感兴趣IEnumerable<T>,您可能需要在数据库术语中考虑它:将其IEnumerable<T>视为表格和IEnumerator<T>光标.您可以要求一个表为您提供一个新游标,并且您可以同时在同一个表上使用多个游标.

这可能需要一段时间才能真正神交这种差异,而只是记住一个列表(或阵列,或其他)没有"你在哪里列表"中的任何概念,但在该列表/数组一个迭代器/什么有一点状态是有帮助的.


Gis*_*shu 8

乔恩说的话.

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


the*_*oop 6

记住,你不具备实施的IEnumerator及其一些变体使用的foreach - IIRC,它需要一切是返回具有的MoveNext()方法返回一个布尔对象的的GetEnumerator()方法,以及当前的财产返还一个东西.您不需要使用IEnumerator和IEnumerable,尽管这样做通常是一个非常好的主意.有关更多信息,请参见此处

  • 是的,很奇怪.我们支持该功能的原因是,您可以在C#1.0中编写一个整数集合,并在没有装箱的情况下枚举它们.现在我们有IE <T>,不再需要"模式"方法了. (3认同)