技术开发 频道

.Net编程接口剖析系列之迭代器


    在感叹C#编译器的智能的时候,我们有必要对yield return的实现做一个更加深入的了解,那就是查看它自动生成的迭代器的代码究竟是怎么样的,与我们自己写的迭代器相比,究竟有多大差别。使用ildasm可以查看IL代码,不过这是非常痛苦的一件事情,即使你对IL的所有指令都背得滚瓜烂熟,同样也是一间非常痛苦的事情。所以,这里我想向大家推荐一个代码学习利器,.Net的反编译器“.Net Reflector”,它可以将编译好的.Net程序反编译为C#代码,并且有着比较高的还原率,而且这个工具本身还在不断升级当中。你可以从http://www.aisto.com/roeder/dotnet免费下载这个软件。
    Yield Return可以在GetEnumerator函数中任意地方使用,而我们的程序结构一般会包括顺序结构、循环和分支选择等。为了弄清编译器对yield return的处理细节,我们先设计一个简单的类,它仅仅使用yield return先返回一个-1,再返回一个100,然后就结束了,这是一个再简单不过的顺序执行结构了,下面是这个类的代码:
class TestYieldReturn:IEnumerable 
{
public IEnumerator GetEnumerator()
{
yield return -1;
yield return 100;
}
}
    我们将这段程序编译好,然后用.NET Reflactor将其进行反编译,于是我们得到了编译器自动生成的迭代器的代码:
[CompilerGenerated] 
private sealed class <GetEnumerator>d__0 : IEnumerator<object>, IEnumerator, IDisposable
{
// Fields
private int <>1__state;
private object <>2__current;
public TestYieldReturn <>4__this;

// Methods
[DebuggerHidden]
public <GetEnumerator>d__0(int <>1__state)
{
this.<>1__state = <>1__state;
}

private bool MoveNext()
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<>2__current = -1;//首先返回-1
this.<>1__state = 1;
return true;

case 1:
this.<>1__state = -1;
this.<>2__current = 100;//再返回100
this.<>1__state = 2;
return true;

case 2:
this.<>1__state = -1; //已经到了末尾,设置状态为结束
break;
}
return false;
}

[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}

void IDisposable.Dispose()
{
}

// Properties
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
}
}
    我们能看到这个迭代器类型的前面有个Attribute:[CompilerGenerated],这表明这个迭代器确实属于编译器生成无疑。迭代器的成员变量除了current和被枚举对象“<>4__this”之外,还增加了一个“<>1__state”的成员,而这个state,真是用于实现顺序程序结构的状态变量,我们可以分析一下最重要的MoveNext函数,State的0、1、2分别代表第一个yield return之前,第一个yield return之后,第二个yield return之后的程序状态,而-1代表枚举结束。编译器就是用state成员变量来控制顺序逻辑的先后次序。
    类似的,C#编译器通过设定一些其他的辅助变量,来实现循环和分支等控制结构。有时候这几种结构是相互组合的,编译器都能够很好的实现他们。
0
相关文章