最近看书时偶然发现的,使用yield return的方法有延迟执行的特性。
先看代码吧:
static void Main(string[] args)
{
var theArray = GetPassedScores();
//如果GetPassedScores被执行了,
//应该能在此WriteLine之前看到输出的字符.
Console.ReadKey();
Console.WriteLine("Invoked...");
foreach (int n in theArray)
{
Console.Write("{0}, ", n);
}
Console.WriteLine();
Console.ReadKey();
}// end Main.
static IEnumerable GetPassedScores()
{
Console.WriteLine("Invoking GetPassedScores()...");
int[] scores = {43, 78, 40, 2, 99, 100, 0, 30, 59, 64, 89};
foreach (int s in scores)
{
if (s >= 60)
{
yield return s;
}
}
}
其中的技术内幕嘛,水平有限,查看IL代码看不懂,郁闷。
在此记下,有待研究<^_^>
//================================================
2013-2-28:好吧,改了标题,但不是太满意,实在不知道该怎么写标题,拗(ao)口了点,将就一下。
终于明白一点了,查看IL代码,发现原来在Program类里多了个内嵌类型
使用Reflector 查看之,其内容如下:
[CompilerGenerated]
private sealed class d__0 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable
{
// Fields
private int <>1__state;
private int <>2__current;
public int[] <>7__wrap4;
public int <>7__wrap5;
private int <>l__initialThreadId;
public int 5__2;
public int[] 5__1;
// Methods
[DebuggerHidden]
public d__0(int <>1__state);
private void <>m__Finally3();
private bool MoveNext();
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator();
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator();
[DebuggerHidden]
void IEnumerator.Reset();
void IDisposable.Dispose();
// Properties
int IEnumerator.Current { [DebuggerHidden] get; }
object IEnumerator.Current { [DebuggerHidden] get; }
}
其实GetPassedScores方法就是返回这个类的一个实例 。把版本切换到 .NET 1.0,查看GetPassedScores方法的内容:
private static IEnumerable GetPassedScores()
{
return new d__0(-2);
}
WriteLine("Invoking GetPassedScores()...");
在生成的类的 MoveNext方法里,包含了生成筛选迭代器元素的代码:
private bool MoveNext()
{
try
{
switch (this.<>1__state)
{
case 0:
this.<>1__state = -1;
Console.WriteLine("Invoking GetPassedScores()...");
this.5__1 = new int[] { 0x2b, 0x4e, 40, 2, 0x63, 100, 0, 30, 0x3b, 0x40, 0x59 };
this.<>1__state = 1;
this.<>7__wrap4 = this.5__1;
this.<>7__wrap5 = 0;
while (this.<>7__wrap5 < this.<>7__wrap4.Length)
{
this.5__2 = this.<>7__wrap4[this.<>7__wrap5];
if (this.5__2 < 60)
{
goto Label_00B1;
}
this.<>2__current = this.5__2;
this.<>1__state = 2;
return true;
Label_00A9:
this.<>1__state = 1;
Label_00B1:
this.<>7__wrap5++;
}
this.<>m__Finally3();
break;
case 2:
goto Label_00A9;
}
return false;
}
fault
{
this.System.IDisposable.Dispose();
}
}
public d__0(int <>1__state)
{
this.<>1__state = <>1__state;
this.<>l__initialThreadId = Thread.CurrentThread.ManagedThreadId;
}
再找一找,在 IEnumerable
IEnumerator IEnumerable.GetEnumerator()
{
if ((Thread.CurrentThread.ManagedThreadId == this.<>l__initialThreadId) && (this.<>1__state == -2))
{
this.<>1__state = 0;
return this;
}
return new Program.d__0(0);
}
而从代码上来看,一旦迭代器被使用一次之后,<>1__state 就应该不可能再变为 -2了,包括上面返回的新实例(其 参数是 0)。所以每次使用这个实例(严格点说,应该是访问到了其中的元素),都是使用 重新生成的一个新实例。
这感觉上和LINQ延迟查询 一样(也许就是一样的,因为LINQ和IEnumerable的关系在那放着。不过我水平有限,不好妄下断言,呵呵),即开篇的那个示例中的变量 theArray 好像不是个变量,倒像是个"委托",使用一次执行一次。
让我们多执行几次来看看吧,修改后的Main方法代码如下:
static void Main(string[] args)
{
var theArray = GetPassedScores();
//如果GetPassedScores被执行了,
//应该能在此WriteLine之前看到输出的字符.
Console.ReadKey();
Console.WriteLine("Invoked...");
Console.WriteLine("1");
foreach (int n in theArray)
{
Console.Write("{0}, ", n);
}
Console.WriteLine();
Console.WriteLine("2");
foreach (int n in theArray)
{
Console.Write("{0}, ", n);
}
Console.WriteLine();
Console.WriteLine("3");
foreach (int n in theArray)
{
Console.Write("{0}, ", n);
}
Console.WriteLine();
Console.ReadKey();
}// end Main.