什么时候应该使用 IEnumerator 在 c# 中循环?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/456433/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 04:04:29  来源:igfitidea点击:

When should I use IEnumerator for looping in c#?

c#iteratorienumerableloops

提问by lomaxx

I was wondering if there are any times where it's advantageous to use an IEnumerator over a foreach loop for iterating through a collection? For example, is there any time where it would be better to use either of the following code samples over the other?

我想知道是否有任何时候在 foreach 循环上使用 IEnumerator 来迭代集合是有利的?例如,是否有任何时候使用以下代码示例中的任何一个比另一个更好?

IEnumerator<MyClass> classesEnum = myClasses.GetEnumerator();
while(classesEnum.MoveNext())
    Console.WriteLine(classesEnum.Current);

instead of

代替

foreach (var class in myClasses)
    Console.WriteLine(class);

采纳答案by Marc Gravell

First, note that one big difference in your example (between foreachand GetEnumerator) is that foreachguarantees to call Dispose()on the iterator if the iterator is IDisposable. This is important for many iterators (which might be consuming an external data feed, for example).

首先,请注意您的示例中(在foreach和之间GetEnumerator)的一个很大区别是,如果迭代器是,则foreach保证调用Dispose()迭代器IDisposable。这对于许多迭代器很重要(例如,它可能正在使用外部数据馈送)。

Actually, there are cases where foreachisn't as helpful as we'd like.

实际上,有些情况下foreach并没有我们希望的那么有用。

First, there is the "first item" case discussed here (foreach / detecting first iteration).

首先,这里讨论了“第一项”情况(foreach/检测第一次迭代)

But more; if you try writing the missing Zipmethod for stitching two enumerable sequences together (or the SequenceEqualmethod), you find that you can' use foreachon both sequences, since that would perform a cross-join. You need to use the iterator directly for one of them:

还有一点; 如果您尝试编写将Zip两个可枚举序列拼接在一起的缺失方法(或SequenceEqual方法),您会发现您不能foreach在两个序列上使用,因为这会执行交叉连接。您需要直接为其中之一使用迭代器:

static IEnumerable<T> Zip<T>(this IEnumerable<T> left,
    IEnumerable<T> right)
{
    using (var iter = right.GetEnumerator())
    {
        // consume everything in the first sequence
        foreach (var item in left)
        {
            yield return item;

            // and add an item from the second sequnce each time (if we can)
            if (iter.MoveNext())
            {
                yield return iter.Current;
            }
        }
        // any remaining items in the second sequence
        while (iter.MoveNext())
        {
            yield return iter.Current;
        }                
    }            
}

static bool SequenceEqual<T>(this IEnumerable<T> left,
    IEnumerable<T> right)
{
    var comparer = EqualityComparer<T>.Default;

    using (var iter = right.GetEnumerator())
    {
        foreach (var item in left)
        {
            if (!iter.MoveNext()) return false; // first is longer
            if (!comparer.Equals(item, iter.Current))
                return false; // item different
        }
        if (iter.MoveNext()) return false; // second is longer
    }
    return true; // same length, all equal            
}

回答by Andy

In C#, foreachis doing exactly the same thing as the IEnumeratorcode that you posted above. The foreachconstruct is provided to make it easier for the programmer. I believe that the IL generated in both cases is the same/similar.

在 C# 中,foreachIEnumerator您在上面发布的代码完全相同。在foreach提供结构,使其更容易为程序员。我相信在这两种情况下生成的 IL 是相同/相似的。

回答by Jay Bazuzi

According to the C# language spec:

根据C# 语言规范

A foreach statement of the form

表单的 foreach 语句

foreach (V v in x) embedded-statement

is then expanded to:

然后扩展为:

{
    E e = ((C)(x)).GetEnumerator();
    try {
            V v;
            while (e.MoveNext()) {
                    v = (V)(T)e.Current;
                    embedded-statement
            }
    }
    finally {
            ... // Dispose e
    }
}

The essense of your two examples are the same, but there are some important differences, most notably the try/finally.

你的两个例子的本质是相同的,但有一些重要的区别,最显着的是try/ finally

回答by Chaowlert Chaisrichalermpol

I used it sometimes when the first iteration do something different from others.

我有时在第一次迭代做与其他迭代不同的事情时使用它。

For example, if you want to print members to console and separate results by line, you can write.

例如,如果要将成员打印到控制台并按行分隔结果,则可以编写。

using (IEnumerator<MyClass> classesEnum = myClasses.GetEnumerator()) {
    if (classEnum.MoveNext())
        Console.WriteLine(classesEnum.Current);
    while (classesEnum.MoveNext()) {
        Console.WriteLine("-----------------");
        Console.WriteLine(classesEnum.Current);
    }
}

Then the result is

那么结果是

myClass 1
-----------------
myClass 2
-----------------
myClass 3

And another situation is when I iterate 2 enumerators together.

另一种情况是当我一起迭代 2 个枚举器时。

using (IEnumerator<MyClass> classesEnum = myClasses.GetEnumerator()) {
    using (IEnumerator<MyClass> classesEnum2 = myClasses2.GetEnumerator()) {
        while (classesEnum.MoveNext() && classEnum2.MoveNext())
            Console.WriteLine("{0}, {1}", classesEnum.Current, classesEnum2.Current);
    }
}

result

结果

myClass 1, myClass A
myClass 2, myClass B
myClass 3, myClass C

Using enumerator allows you to do more flexible on your iteration. But in most case, you can use foreach

使用枚举器可以让您在迭代中更加灵活。但在大多数情况下,您可以使用 foreach