实现C#产出语句的算法

问题描述:

我很想弄清楚自己,但我想知道粗略地说,将函数与yield语句转换为枚举器的状态机的算法是什么?例如如何做C#关闭此:实现C#产出语句的算法

IEnumerator<string> strings(IEnumerable<string> args) 
{ IEnumerator<string> enumerator2 = getAnotherEnumerator();  
    foreach(var arg in arg) 
    { enumerator2.MoveNext(); 
     yield return arg+enumerator.Current; 
    } 
} 

成这样:

bool MoveNext() 
{ switch (this.state) 
    { 
     case 0: 
      this.state = -1; 
      this.enumerator2 = getAnotherEnumerator(); 
      this.argsEnumerator = this.args.GetEnumerator(); 
      this.state = 1; 
      while (this.argsEnumerator.MoveNext()) 
      { 
       this.arg = this.argsEnumerator.Current; 
       this.enumerator2.MoveNext(); 
       this.current = this.arg + this.enumerator2.Current; 
       this.state = 2; 
       return true; 

       state1: 
       this.state = 1; 
      } 
      this.state = -1; 
      if (this.argsEnumerator != null) this.argsEnumerator.Dispose(); 
      break; 

     case 2: 
      goto state1; 
    } 
    return false; 
} 

结果当然可以根据原始代码是完全不同的。

你正在寻找的特定代码示例涉及一系列转换。 请注意,这是对算法的近似描述。编译器使用的实际名称以及它生成的确切代码可能不同。然后,想法是一样的。

第一变换是“的foreach”变换,其将这样的代码:

foreach (var x in y) 
{ 
    //body 
} 

到这个代码:

var enumerator = y.GetEnumerator(); 
while (enumerator.MoveNext()) 
{ 
    var x = enumerator.Current; 
    //body 
} 

if (y != null) 
{ 
    enumerator.Dispose(); 
} 

第二变换查找所有的功能体的产率return语句,为每个(状态值)分配一个数字,并在产出后立即创建一个“goto标签”。

第三个转换将方法体中的所有局部变量和函数参数提升为一个称为闭包的对象。

鉴于您的示例代码,这将类似于此:

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; 
     } 
    } 
} 

的方法体,然后从原来的方法转移,到内“封闭”的方法叫的MoveNext,它返回一个布尔,并实现IEnumerable.MoveNext。 对任何本地人的任何访问都通过“this”进行路由,并且对任何类成员的任何访问均通过this.original路由。

任何 “收益率回报EXPR” 被翻译成:

currentValue = expr; 
state = //the state number of the yield statement; 
return true; 

任何产量break语句被翻译成:

state = -1; 
return false; 

有一个在的末端的 “隐含的” yield break语句功能。 然后在查看状态号的过程开始处引入一个switch语句并跳转到关联的标号。

的原始方法,然后翻译成这样的:

IEnumerator<string> strings(IEnumerable<string> args) 
{ 
    return new ClosureEnumerable(this,args); 
} 

,该方法的状态都推入对象和MoveNext方法使用switch语句的事实/状态变量是什么允许迭代器的行为就像在下次调用“MoveNext”时最后一个“yield return”语句之后立即传回控制点一样。

有必要指出的是很重要的,但是,由C#编译器使用的转型是不是要做到这一点的最好办法。在尝试使用递归算法使用“yield”时,它的性能很差。有一个很好的文件,概述了一种更好的方式,在这里做这样的:

http://research.microsoft.com/en-us/projects/specsharp/iterators.pdf

如果你还没有读过这是值得一读。它最近我wrote an article -

Raymond chen回答了这个问题; http://blogs.msdn.com/b/oldnewthing/archive/2008/08/12/8849519.aspx

(编辑以指向系列,而不是部分4的部分1)

+1

Raymond Chen只是说“这很神奇” – 2008-09-25 07:34:00

+0

我认为marxidad想弄清楚编译器用来解释和转换的算法是什么迭代器的C#代码阻塞到对应于状态机的IL中。 – 2008-09-25 07:54:57

就发现了这个问题。我将不得不将这里提到的其他链接添加到文章中...