Sai*_*ait 7 c# foreach enumerable enumerator
我基本上试图使我的类能够迭代使用foreach.我读了这个教程.MSDN.看起来很直接.但是,当我想第二次迭代时,我遇到了问题.我调试了它; 事实证明它没有调用该Reset()函数.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
当我运行以下主要功能时; 它从不调用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);
}
}
Run Code Online (Sandbox Code Playgroud)
0
1
2
3
4
--- First foreach finished. ---
Press any key to continue . . .
Run Code Online (Sandbox Code Playgroud)
有什么想法吗?
Ree*_*sey 13
每次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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,您也可以直接返回数组的枚举器(虽然我假设您正在尝试学习如何创建干净的枚举器):
class A : IEnumerable
{
int[] data = { 0, 1, 2, 3, 4 };
public IEnumerator GetEnumerator()
{
return data.GetEnumerator();
}
}
Run Code Online (Sandbox Code Playgroud)
最后,您可以使用迭代器以更简单的方式实现它:
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];
}
}
Run Code Online (Sandbox Code Playgroud)
话虽这么说,我强烈建议IEnumerable<int>除了IEnumerable之外还要实现.仿制药在使用方面做得更好.
Reset() 基本上是一个错误。如果可能的话,已经有一个已知的方法来获得一个干净的枚举器:GetEnumerator()。
这是一个需要在迭代器块实现(迭代器)规范抛出一个异常,此方法,因此它是一般情况下的正式名称,它不能指望工作,简单地说:没有人会调用它。坦率地说,他们也应该在 API 上将其标记为 [Obsolete]!
此外,许多序列是不可重复的。想想坐在 NetworkStream 或随机数字生成器上的迭代器。因为迭代器(在一般情况下)不需要是可重复的,所以您应该尽可能地将它们最多迭代一次。如果不可能,也许可以通过 ToList() 进行缓冲。
“foreach”在任何时候都不涉及 Reset()。只需 GetEnumerator()、MoveNext()、Current 和 Dispose()。
枚举不调用Reset. 您需要创建一个枚举器的新实例,这可能意味着为枚举器创建一个单独的类(即,不为 IEnumerable 和 IEnumerator 使用相同的类型),如下面的代码所示:
public class StackOverflow_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);
}
}
}
Run Code Online (Sandbox Code Playgroud)