我实现 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 中同时实现 IEnumerable
和 IEnumerator
。 (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
关键字几乎不需要显式实现 IEnumerator
或 IEnumerable
:
public IEnumerator<string> GetEnumerator()
{
foreach(string s in Groups)
yield return s;
}
我有一个存储字符串列表的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 中同时实现 IEnumerable
和 IEnumerator
。 (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
关键字几乎不需要显式实现 IEnumerator
或 IEnumerable
:
public IEnumerator<string> GetEnumerator()
{
foreach(string s in Groups)
yield return s;
}