下面是我制作的自定义类:
{{- if !$e.ExcludeFromIndex -}}
如果我们遍历foreach:
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;
}
}
}
输出是
B b = new B();
foreach (var item in b)
{
Console.WriteLine(item);
}
foreach (var item in b)
{
Console.WriteLine(item);
}
我们可以看到GetEnumerator()被调用了两次,因为我们使用了两个foreach循环,每个foreach都调用一次GetEnumerator(),就足够了。
但是如果我们将迭代器修改为
called
0
1
2
3
4
called
0
1
2
3
4
很容易看到GetEnumerator()将被调用五次以获取每个值。那么,为什么GetEnumerator()仅被调用一次,有时却被多次调用,这是不一致的?
P.S 我知道以下事实:如果您使用yield运行代码,结果是相同的,并且GetEnumerator()似乎被调用了两次,但是由于yield是特殊的,这使得整个方法似乎对于每个foreach都只调用一次,但是该方法必须在后台多次调用(在这种情况下,GetEnumerator()将被调用10次)
答案 0 :(得分:3)
非常简单地输入(并且不考虑GetEnumerator
被称为相同次数的事实),yield
是特例...
文档摘录示例的摘录
在foreach循环的迭代中,调用MoveNext方法 元素。 此调用执行MyIteratorMethod的主体 直到到达下一个收益率声明为止。表达方式 由yield return语句返回的值不仅确定值 循环体消耗的元素变量的数量,以及 元素的当前属性
编译器为方法生成代码,并探查实现IEnumerator
进行枚举的类 (就像您所拥有的一样),as you can see here
注意 :编译器会为您生成代码并做一些您不能做的特殊事情(可能称为不一致)
给出此
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();
}
}