あなたが見ている特定のコードサンプルには、一連の変換が含まれています。これはアルゴリズムのおおよその説明であることに注意してください。コンパイラが使用する実際の名前と、コンパイラが生成する正確なコードは異なる場合があります。ただし、考え方は同じです。
最初の変換は「foreach」変換であり、次のコードを変換します。
foreach (var x in y)
{
//body
}
このコードに:
var enumerator = y.GetEnumerator();
while (enumerator.MoveNext())
{
var x = enumerator.Current;
//body
}
if (y != null)
{
enumerator.Dispose();
}
2番目の変換では、関数本体ですべてのyield returnステートメントを検索し、それぞれに番号(状態値)を割り当て、yieldの直後に「gotoラベル」を作成します。
3番目の変換は、メソッド本体のすべてのローカル変数と関数の引数をクロージャと呼ばれるオブジェクトに持ち上げます。
あなたの例のコードを考えると、それは次のようになります。
class ClosureEnumerable : IEnumerable<string>
{
private IEnumerable<string> args;
private ClassType originalThis;
public ClosureEnumerator(ClassType origThis, IEnumerable<string> args)
{
this.args = args;
this.origianlThis = origThis;
}
public IEnumerator<string> GetEnumerator()
{
return new Closure(origThis, args);
}
}
class Closure : IEnumerator<string>
{
public Closure(ClassType originalThis, IEnumerable<string> args)
{
state = 0;
this.args = args;
this.originalThis = originalThis;
}
private IEnumerable<string> args;
private IEnumerator<string> enumerator2;
private IEnumerator<string> argEnumerator;
//- Here ClassType is the type of the object that contained the method
// This may be optimized away if the method does not access any
// class members
private ClassType originalThis;
//This holds the state value.
private int state;
//The current value to return
private string currentValue;
public string Current
{
get
{
return currentValue;
}
}
}
次に、メソッド本体が元のメソッドから「Closure」内のMoveNextというメソッドに移動されます。これにより、ブール値が返され、IEnumerable.MoveNextが実装されます。ローカルへのアクセスは「this」を介してルーティングされ、クラスメンバーへのアクセスはthis.originalThisを介してルーティングされます。
「yieldreturnexpr」は、次のように変換されます。
currentValue = expr;
state = //the state number of the yield statement;
return true;
すべてのyieldbreakステートメントは次のように変換されます。
state = -1;
return false;
関数の最後に「暗黙の」yieldbreakステートメントがあります。次に、プロシージャの最初にswitchステートメントが導入され、状態番号を調べて、関連付けられたラベルにジャンプします。
その後、元のメソッドは次のように変換されます。
IEnumerator<string> strings(IEnumerable<string> args)
{
return new ClosureEnumerable(this,args);
}
メソッドの状態がすべてオブジェクトにプッシュされ、MoveNextメソッドがswitchステートメント/状態変数を使用するという事実により、イテレーターは、最後の「yieldreturn」の直後のポイントに制御が戻されているかのように動作できます。次回「MoveNext」が呼び出されたときの「ステートメント」。
ただし、C#コンパイラで使用される変換は、これを行うための最良の方法ではないことを指摘することが重要です。再帰的アルゴリズムで「yield」を使おうとすると、パフォーマンスが低下します。これを行うためのより良い方法を概説した良い論文がここにあります:
http://research.microsoft.com/en-us/projects/specsharp/iterators.pdf
まだ読んでいない場合は、読む価値があります。