Конкретный пример кода, на который Вы смотрите, включает ряд преобразований. Обратите внимание на то, что это - приблизительное описание алгоритма. Подлинные имена, используемые компилятором и точным кодом, который это генерирует, могут отличаться. Идея является тем же, как бы то ни было.
первое преобразование является "foreach" преобразованием, которое преобразовывает этот код:
foreach (var x in y)
{
//body
}
в этот код:
var enumerator = y.GetEnumerator();
while (enumerator.MoveNext())
{
var x = enumerator.Current;
//body
}
if (y != null)
{
enumerator.Dispose();
}
второе преобразование находит все операторы возврата урожая в теле функции, присваивает номер каждому (значение состояния) и создает "goto, маркируют" right after the yield.
The third transformation lifts all the local variables and function arguments in the method body into an object called a closure.
Given the code in your example, that would look similar to this:
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;
}
}
}
The method body is then moved from the original method to a method inside "Closure" called MoveNext, which returns a bool, and implements IEnumerable. MoveNext. Any access to any locals is routed through "this", and any access to any class members are routed through this.originalThis.
Any "expr возврата урожая", переводится в:
currentValue = expr;
state = //the state number of the yield statement;
return true;
Любой оператор завершения урожая переводится в:
state = -1;
return false;
существует "неявный" оператор завершения урожая в конце функции. Оператор переключения затем представлен в начале процедуры, которая смотрит на число состояния и переходы к связанной маркировке.
исходный метод затем переводится во что-то вроде этого:
IEnumerator<string> strings(IEnumerable<string> args)
{
return new ClosureEnumerable(this,args);
}
то, что состояние метода все продвинуто в объект и что метод MoveNext использует оператор переключения / переменная состояния - то, что позволяет итератору вести себя, как будто управление пасуется назад к точке сразу после последнего "оператора" возврата урожая, которым называют следующий раз "MoveNext".
важно указать, однако, что преобразование, используемое компилятором C#, не является лучшим способом сделать это. Это страдает от низкой производительности при попытке использовать "урожай" с рекурсивными алгоритмами. Существует хорошая статья, которая обрисовывает в общих чертах лучший способ сделать это здесь:
http://research.microsoft.com/en-us/projects/specsharp/iterators.pdf
Это стоит чтения, если Вы еще не считали его.
Просто определенный этот вопрос - я записал статью о нем недавно. Я должен буду добавить другие ссылки, упомянутые здесь статье хотя...
Raymond chen отвечает на это; http://blogs.msdn.com/b/oldnewthing/archive/2008/08/12/8849519.aspx
(отредактированный для указания на часть 1 ряда, не часть 4)