好的,当我在构建自定义枚举器时,我注意到这种与产量
相关的行为说你有这样的事情:
public class EnumeratorExample
{
public static IEnumerable<int> GetSource(int startPoint)
{
int[] values = new int[]{1,2,3,4,5,6,7};
Contract.Invariant(startPoint < values.Length);
bool keepSearching = true;
int index = startPoint;
while(keepSearching)
{
yield return values[index];
//The mind reels here
index ++
keepSearching = index < values.Length;
}
}
}
在技术上从函数返回后,是什么使编译器的底层可以执行index ++和while循环中的其余代码?
答案 0 :(得分:9)
编译器将代码重写为状态机。您编写的单个方法分为不同的部分。每次调用MoveNext
(隐含或明确)时,状态都会提前,并执行正确的代码块。
如果您想了解更多详情,建议阅读:
答案 1 :(得分:4)
编译器代表您生成状态机。
来自语言规范:
10.14迭代器
10.14.4枚举器对象
当一个函数成员返回时 枚举器接口类型是 使用迭代器块实现, 调用函数成员不会 立即执行中的代码 迭代器块。相反,一个普查员 创建并返回对象。这个 object封装指定的代码 在迭代器块中,执行 迭代器块中的代码 在枚举器对象的时候发生 调用MoveNext方法。一个 枚举器对象具有以下内容 特性:
•实施 IEnumerator和IEnumerator,在哪里 T是迭代器的yield类型。
•它实现了System.IDisposable。
•初始化了一份副本 参数值(如果有)和实例 传递给函数成员的值。
•它有四种潜在的状态, 之前,跑步,停赛和之后, 并且最初处于之前状态。
枚举器对象通常是一个 编译器生成的实例 封装了的枚举器类 迭代器块中的代码和 实现枚举器接口, 但其他实施方法 是可能的。如果是枚举器类 由编译器生成 class将直接嵌套或 间接地,在包含的类中 功能成员,它会有 私人无障碍,它会 有一个名称保留供编译器使用 (第2.4.2节)。
为了弄清楚这一点,这里是Reflector如何反编译你的类:
public class EnumeratorExample
{
// Methods
public static IEnumerable<int> GetSource(int startPoint)
{
return new <GetSource>d__0(-2) { <>3__startPoint = startPoint };
}
// Nested Types
[CompilerGenerated]
private sealed class <GetSource>d__0 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable
{
// Fields
private int <>1__state;
private int <>2__current;
public int <>3__startPoint;
private int <>l__initialThreadId;
public int <index>5__3;
public bool <keepSearching>5__2;
public int[] <values>5__1;
public int startPoint;
// Methods
[DebuggerHidden]
public <GetSource>d__0(int <>1__state)
{
this.<>1__state = <>1__state;
this.<>l__initialThreadId = Thread.CurrentThread.ManagedThreadId;
}
private bool MoveNext()
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<values>5__1 = new int[] { 1, 2, 3, 4, 5, 6, 7 };
this.<keepSearching>5__2 = true;
this.<index>5__3 = this.startPoint;
while (this.<keepSearching>5__2)
{
this.<>2__current = this.<values>5__1[this.<index>5__3];
this.<>1__state = 1;
return true;
Label_0073:
this.<>1__state = -1;
this.<index>5__3++;
this.<keepSearching>5__2 = this.<index>5__3 < this.<values>5__1.Length;
}
break;
case 1:
goto Label_0073;
}
return false;
}
[DebuggerHidden]
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
EnumeratorExample.<GetSource>d__0 d__;
if ((Thread.CurrentThread.ManagedThreadId == this.<>l__initialThreadId) && (this.<>1__state == -2))
{
this.<>1__state = 0;
d__ = this;
}
else
{
d__ = new EnumeratorExample.<GetSource>d__0(0);
}
d__.startPoint = this.<>3__startPoint;
return d__;
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return this.System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
void IDisposable.Dispose()
{
}
// Properties
int IEnumerator<int>.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
}
}
}
答案 2 :(得分:2)
答案 3 :(得分:2)
这是一个优秀的博客系列(来自微软资深人士Raymond Chen),详细介绍了收益率如何运作:
第1部分:http://blogs.msdn.com/b/oldnewthing/archive/2008/08/12/8849519.aspx
第2部分:http://blogs.msdn.com/b/oldnewthing/archive/2008/08/13/8854601.aspx
第3部分:http://blogs.msdn.com/b/oldnewthing/archive/2008/08/14/8862242.aspx
第4部分:http://blogs.msdn.com/b/oldnewthing/archive/2008/08/15/8868267.aspx
答案 4 :(得分:2)
这是C#编译器中最复杂的部分之一。最好阅读Jon Skeet的 C#深度的免费样本章节(或者更好,阅读本书并阅读: - )
有关进一步说明,请参阅Marc Gravell的回答: