ziz*_*raj 23 c# design-patterns
IEnumerator包含MoveNext(),Reset()并Current作为其成员.现在假设我已将这些方法和属性移动到IEnumerable接口并删除了GetEnumerator()方法和IEnumerator接口.
现在,实现的类的对象IEnumerable将能够访问方法和属性,因此可以迭代.
IEnumerator接口的存在如何解决这些问题?Jon*_*eet 49
迭代器包含与集合分离的状态:它包含一个光标,表示您在集合中的位置.因此,必须有一个独立的对象来表示额外的状态,一种方法来获取该对象和操作上的那个对象-因此IEnumerator(和IEnumerator<T>),GetEnumerator()和迭代器的成员.
想象一下,如果我们没有单独的状态,那么我们写道:
var list = new List<int> { 1, 2, 3 };
foreach (var x in list)
{
foreach (var y in list)
{
Console.WriteLine("{0} {1}", x, y);
}
}
Run Code Online (Sandbox Code Playgroud)
这应该打印"1 1","1 2","1 3","2 1"等...但没有任何额外的状态,它怎么能"知道"两个循环的两个不同位置?
Tho*_*que 25
现在假设我已经将这些方法和属性移动到IEnumerable接口并删除了GetEnumerator()方法和IEnumerator接口.
这样的设计会阻止集合中的并发枚举.如果是跟踪当前位置的集合本身,则不能有多个枚举相同集合的线程,甚至是嵌套的枚举,例如:
foreach (var x in collection)
{
foreach (var y in collection)
{
Console.WriteLine("{0} {1}", x, y);
}
}
Run Code Online (Sandbox Code Playgroud)
通过将跟踪当前位置的责任委托给不同的对象(枚举器),它使集合的每个枚举独立于其他对象.
一个很长的答案,前两个答案涵盖了大部分答案,但我发现在C#语言规范中查找foreach时我发现有些方面很有趣.除非您对此感兴趣,否则请停止阅读.
现在转到intering部分,根据C#规范扩展以下声明:
foreach (V v in x) embedded-statement
Run Code Online (Sandbox Code Playgroud)
祝你:
{`
E e = ((C)(x)).GetEnumerator();
try {
while (e.MoveNext()) {
V v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
Run Code Online (Sandbox Code Playgroud)
拥有某种身份函数x == ((C)(x)).GetEnumerator()(它是它自己的枚举器)并使用@ JonSkeet的循环产生类似的东西(为了简洁,删除了try/catch):
var list = new List<int> { 1, 2, 3 };
while (list.MoveNext()) {
int x = list.Current; // x is always 1
while (list.MoveNext()) {
int y = list.Current; // y becomes 2, then 3
Console.WriteLine("{0} {1}", x, y);
}
}
Run Code Online (Sandbox Code Playgroud)
将打印出以下内容:
1 2
1 3
Run Code Online (Sandbox Code Playgroud)
然后list.MoveNext()将false永远回归.如果你看一下,这一点非常重要:
var list = new List<int> { 1, 2, 3 };
// loop
foreach (var x in list) Console.WriteLine(x); // Will print 1,2,3
// loop again
// Will never enter loop, since reset wasn't called and MoveNext() returns false
foreach (var y in list) Console.WriteLine(x); // Print nothing
Run Code Online (Sandbox Code Playgroud)
因此,考虑到上述情况,并注意它完全可行,因为foreach语句GetEnumerator()在检查类型是否实现之前查找-method IEnumerable<T>:
为什么没有遵循上述方法以及如果我遵循它将面临的问题?
您不能嵌套循环,也不能使用该foreach语句多次访问同一个集合而不Reset()在它们之间手动调用.当我们在每次之后处理枚举器时会发生什么foreach?
IEnumerator接口的存在如何解决这些问题?
所有迭代都是相互独立的,无论我们是在讨论嵌套,多线程等,枚举都与集合本身是分开的.您可以将其视为有点像关注点或SoC,因为想法是将遍历与实际列表本身分开,并且在任何情况下遍历都不应该改变集合的状态.IE调用MoveNext()您的示例将修改集合.