C# IEnumerable,IEnumerator 重置函数未被调用
Posted
技术标签:
【中文标题】C# IEnumerable,IEnumerator 重置函数未被调用【英文标题】:C# IEnumerable, IEnumerator Reset Function Not Get Called 【发布时间】:2012-07-13 14:09:41 【问题描述】:我基本上是想让我的班级能够使用foreach
进行迭代。我读了这个教程。 MSDN。这似乎很简单。但是,当我想第二次迭代时遇到问题。我调试了它;结果它没有调用Reset()
函数。
A 类
class A : IEnumerable, IEnumerator
int[] data = 0, 1, 2, 3, 4 ;
int position = -1;
public object Current
get
return data[position];
public bool MoveNext()
position++;
return (position < data.Length);
public void Reset()
position = -1;
public IEnumerator GetEnumerator()
return (IEnumerator)this;
当我运行以下主要功能时;它从不调用Reset()
函数。所以,在一个循环之后,我再也无法迭代我的类了。
主要
static void Main(string[] args)
A a = new A();
foreach (var item in a)
Console.WriteLine(item);
Console.WriteLine("--- First foreach finished. ---");
foreach (var item in a)
Console.WriteLine(item);
输出:
0
1
2
3
4
--- First foreach finished. ---
Press any key to continue . . .
有什么想法吗?
【问题讨论】:
看看这个answer 顺便说一句,除非你是为了好玩或一个非常具体的原因,否则你永远不必以这种方式实现迭代器。查看“迭代器块”又名“收益回报”。 我知道它很旧,但我刚刚在谷歌上找到了这篇文章。我遇到了同样的问题,我实现了 MoveNext 方法,当它返回 false 时调用 Reset。 【参考方案1】:里德和卡洛斯都是正确的。这是您可以做到这一点的一种方法,因为int[]
实现了IEnumerable
和IEnumerable<int>
class A : IEnumerable
int[] data = 0, 1, 2, 3, 4 ;
public IEnumerator GetEnumerator()
return data.GetEnumerator();
或者,为了更加强类型,你可以使用IEnumerable
的泛型形式:
class A : IEnumerable<int>
int[] data = 0, 1, 2, 3, 4 ;
public IEnumerator<int> GetEnumerator()
return ((IEnumerable<int>)data).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
return data.GetEnumerator();
【讨论】:
我的真实情况比较复杂。这只是一个解释我的问题的例子。在真正的问题中,我没有int
数组。
@Zagy 酷。那么,里德和卡洛斯都有很好的答案。 :)【参考方案2】:
每次调用foreach
,它都会要求一个新的 IEnumerator
。返回你的类实例是个坏主意——你应该创建一个单独的类来实现IEnumerator
,然后返回它。
这通常通过使用嵌套(私有)类并返回它的实例来完成。您可以将 A 类实例传递给私有类(授予它对 data
的访问权限),并将 position
字段放在该类中。它将允许同时创建多个枚举器,并且可以在后续的 foreach 调用中正常工作。
例如,要修改您的代码,您可以执行以下操作:
using System;
using System.Collections;
class A : IEnumerable
int[] data = 0, 1, 2, 3, 4 ;
public IEnumerator GetEnumerator()
return new AEnumerator(this);
private class AEnumerator : IEnumerator
public AEnumerator(A inst)
this.instance = inst;
private A instance;
private int position = -1;
public object Current
get
return instance.data[position];
public bool MoveNext()
position++;
return (position < instance.data.Length);
public void Reset()
position = -1;
请注意,您也可以直接返回数组的枚举数(尽管我假设您正在尝试学习如何制作干净的枚举数):
class A : IEnumerable
int[] data = 0, 1, 2, 3, 4 ;
public IEnumerator GetEnumerator()
return data.GetEnumerator();
最后,您可以使用iterators 以更简单的方式实现这一点:
class A : IEnumerable
int[] data = 0, 1, 2, 3, 4 ;
public IEnumerator GetEnumerator()
for (int i=0;i<data.Length;++i)
yield return data[i];
话虽如此,我强烈建议在 IEnumerable 之外实现 IEnumerable<int>
。泛型在使用方面做得更好。
【讨论】:
是的,你是对的。我试图学习如何制作干净的枚举器。我的真实代码更复杂。 @zagy 看看迭代器——它们通常是实现枚举器最好、最实用的方法。话虽这么说,除了IEnumerable
之外,我也总是实现IEnumerable<T>
...【参考方案3】:
Reset() 基本上是一个错误。如果可能的话,已经有一个已知的方法来获取一个干净的枚举器:GetEnumerator()。
在规范中要求迭代器块实现(对于迭代器)为此方法抛出异常,因此在一般情况下,正式知道不能指望它起作用,而且很简单:没有人调用它。坦率地说,他们也应该在 API 上将其标记为 [已过时]!
此外,许多序列是不可重复的。考虑一下位于 NetworkStream 或随机数生成器上的迭代器。因为迭代器(在一般情况下)不需要是可重复的,所以您应该尽可能地针对它们最多迭代一次。如果不可能的话,也许可以通过 ToList() 进行缓冲。
“foreach”在任何时候都不涉及 Reset()。只需 GetEnumerator()、MoveNext()、Current 和 Dispose()。
【讨论】:
【参考方案4】:枚举不调用Reset
。您需要创建一个枚举器的新实例,这可能意味着为枚举器创建一个单独的类(即,IEnumerable 和 IEnumerator 不使用相同的类型),如下面的代码所示:
public class ***_11475328
class A : IEnumerable
int[] data = 0, 1, 2, 3, 4 ;
public IEnumerator GetEnumerator()
return new AEnumerator(this);
class AEnumerator : IEnumerator
private A parent;
private int position = -1;
public AEnumerator(A parent)
this.parent = parent;
public object Current
get return parent.data[position];
public bool MoveNext()
position++;
return (position < parent.data.Length);
public void Reset()
position = -1;
public static void Test()
A a = new A();
foreach (var item in a)
Console.WriteLine(item);
Console.WriteLine("--- First foreach finished. ---");
foreach (var item in a)
Console.WriteLine(item);
【讨论】:
以上是关于C# IEnumerable,IEnumerator 重置函数未被调用的主要内容,如果未能解决你的问题,请参考以下文章