我实现 IEnumerator 和 IEnumerable 的 class 没有转到 foreach 语句

My class that implements IEnumerator and IEnumerable doesn't go to foreach statement

我有一个存储字符串列表的class,我想让这个class在foreach语句中可用,所以我找到了这两个接口并尝试实现它们。

public class GroupCollection : IEnumerable, IEnumerator
{
    public List<string> Groups { get; set; }
    public int Count { get { return Groups.Count; } }
    int position = -1;
}

public IEnumerator GetEnumerator()
{
    return (IEnumerator)this;
}

public object Current
{
    get
    {
        try
        {
            return new Group(Groups[position]);
        }
        catch (IndexOutOfRangeException)
        {
            throw new InvalidOperationException();
        }
    }
}

public bool MoveNext()
{
    position++;
    return position < Groups.Count;
}

public void Reset()
{
    position = 0;
}

我正在对 GroupCollection 变量进行两次迭代:

foreach (GroupCollection.Group in groups) // where groups is a GroupCollection
{
}

foreach (GroupCollection.Group in groups)
{
}

// where Group is a nested class in GroupCollection.

当它在第一个 foreach 时效果很好(此时计数为 1)。我不修改任何东西,当它进入第二个 foreach 时,它不会进入循环。我在调试模式下逐行检查代码,发现在第一个 foreach 之后没有调用重置。那么我应该在 foreach 之后手动调用 reset 吗?有没有更好的方法来做到这一点?

I don't modify anything

是的,您这样做了 - 您的 MoveNext() 修改了 class 的状态。这就是为什么您不应该在同一个 class 中同时实现 IEnumerableIEnumerator。 (C# 编译器会处理迭代器块,但这是特例。)您应该能够调用 GetEnumerator() 两次并获得两个完全独立的迭代器。例如:

foreach (var x in collection)
{
    foreach (var y in collection)
    {
        Console.WriteLine("{0}, {1}", x, y);
    }
}

... 应该为您提供集合中所有可能的项目对。但是 只有 在迭代器独立时有效。

I went through the code line by line in debugging mode and found out that the reset is not called after the first foreach.

为什么你会期望它?我不相信规范中有任何关于 foreach 调用 Reset 的内容 - 这是一个很好的工作,因为许多实现并没有 真正地 实现它(他们抛出一个例外)。

基本上,您应该使您的 GetEnumerator() 方法 return 成为一个新对象,该对象在您的数据上保持 "cursor" 的可变状态。请注意,在 C# 中实现迭代器的最简单方法是 通常 使用迭代器块(yield return 等)。

我还强烈鼓励您实现通用接口,而不仅仅是非通用接口;这样你的类型可以更容易地在 LINQ 代码中使用,foreach 语句中的迭代器变量可以适当地隐式键入,等等

Reset 不会在 foreach 循环结束时调用 - 您 可以 GetEnumerator 调用中这样做,或者只是return List 的枚举数:

public IEnumerator GetEnumerator()
{
    return Groups.GetEnumerator;
}

请注意,使用 yield 关键字几乎不需要显式实现 IEnumeratorIEnumerable

public IEnumerator<string> GetEnumerator()
{
    foreach(string s in Groups)
        yield return s;
}