c# - 使用 foreach 循环时调用 GetEnumerator() 多少次
问题描述
以下是我制作的自定义类:
class B : IEnumerable
{
int[] data = { 0, 1, 2, 3, 4 };
public IEnumerator GetEnumerator()
{
Console.WriteLine("Called");
return new BEnumerator(this);
}
private class BEnumerator : IEnumerator
{
private B instance;
private int position = -1;
public BEnumerator(B inst)
{
this.instance = inst;
}
public object Current
{
get
{
return instance.data[position];
}
}
public bool MoveNext()
{
position++;
return (position < instance.data.Length);
}
public void Reset()
{
position = -1;
}
}
}
如果我们遍历foreach:
B b = new B();
foreach (var item in b)
{
Console.WriteLine(item);
}
foreach (var item in b)
{
Console.WriteLine(item);
}
输出是
called
0
1
2
3
4
called
0
1
2
3
4
我们可以看到 GetEnumerator() 被调用了两次,因为我们使用了两个 foreach 循环,每个 foreach 调用 GetEnumerator() 一次,这很公平。
但是如果我们将迭代器修改为
public IEnumerator GetEnumerator()
{
yield return data[0];
yield return data[1];
yield return data[2];
yield return data[3];
yield return data[4];
}
很容易看出,GetEnumerator() 将被调用五次以获取每个值。那么为什么 GetEnumerator() 只调用一次,有时调用多次,这是不一致的呢?
PS我知道如果你用yield运行代码,结果是一样的,GetEnumerator()似乎被调用了两次,但是因为yield是特殊的,这使得整个方法似乎每次foreach只调用一次,但是该方法必须在后台多次调用(在这种情况下 GetEnumerator() 将被调用 10 次)
解决方案
简单地说(并且忽略GetEnumerator
被称为相同次数的事实),yield
是一种特殊情况......
摘自文档中给出的示例
在 foreach 循环的迭代中,为元素调用 MoveNext 方法。此调用执行 MyIteratorMethod 的主体,直到到达下一个 yield return 语句。yield return语句返回的表达式不仅决定了循环体消耗的元素变量的值,还决定了元素的Current属性
编译器为该方法生成代码并查找一个实现枚举的类(就像您拥有的那样),如您在此处看到的IEnumerator
注意:编译器为您生成代码并执行您无法执行的特殊操作(您可能称之为不一致)的地方很多
鉴于这种
public IEnumerator GetEnumerator()
{
yield return data[0];
yield return data[1];
yield return data[2];
yield return data[3];
yield return data[4];
}
编译器像这样生成你的方法
[IteratorStateMachine(typeof(<GetEnumerator>d__1))]
public IEnumerator GetEnumerator()
{
<GetEnumerator>d__1 <GetEnumerator>d__ = new <GetEnumerator>d__1(0);
<GetEnumerator>d__.<>4__this = this;
return <GetEnumerator>d__;
}
并生成这样的类
[CompilerGenerated]
private sealed class <GetEnumerator>d__1 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public C <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <GetEnumerator>d__1(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
}
private bool MoveNext()
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<>2__current = <>4__this.data[0];
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
<>2__current = <>4__this.data[1];
<>1__state = 2;
return true;
case 2:
<>1__state = -1;
<>2__current = <>4__this.data[2];
<>1__state = 3;
return true;
case 3:
<>1__state = -1;
<>2__current = <>4__this.data[3];
<>1__state = 4;
return true;
case 4:
<>1__state = -1;
<>2__current = <>4__this.data[4];
<>1__state = 5;
return true;
case 5:
<>1__state = -1;
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
推荐阅读
- kubernetes - 入口资源未路由正确公开的服务
- c# - 连接到 NetSuite Web 服务时出现 SOAP 错误:“未定义命名空间前缀 'soapenv'”
- mongodb - mongo 和 laravel 之间没有使用 docker 的连接
- testing - 拥有 1000 多个节点的 Jenkins 性能 - 自己的设备场
- javascript - TypeError: $(...).on 不是prototype.js 的函数
- react-native - 集成(Redux 与本机反应)
- android - 在 Oreo 中将位图重力设置为底部离开屏幕层列表
- javascript - 验证表单上的两个字段
- mysql - 在 sql developer 中更改连接 url
- android - 未获取匿名数组列表的数据