https://startbigthinksmall.wordpress.com/2008/06/09/behind-the-scenes-of-the-c-yield-keyword/

Behind the scenes of the C# yield keyword

June 9, 2008 by Lars Corneliussen

After reading the great article about the code-saving yield keyword “Give way to the yield keyword” by Shay Friedman I thought it could be interesting to know how the yield keyword works behind the scenes.

…it doesn’t really end the method’s execution. yield return pauses the method execution and the next time you call it (for the next enumeration value), the method will continue to execute from the last yield return call. It sounds a bit confusing I think… (ShayF)

By using yield return within a method that returns IEnumerable or IEnumeratorthe language feature is activated.

Note: IEnumerable is kind of a stateless factory for Enumerators.IEnumerable.GetEnumerator() is thread safe and can be called multiple times, while the returned stateful Enumerator is just a helper for enumerating contained values once. By contract IEnumerator offers a Reset() method, but many implementations just throw a NotSupportedException.

Lets create an enumerator method that yields some Fibonacci nubmers.

public class YieldingClass
{
public IEnumerable<int> GetFibonachiSequence()
{
yield return ;
yield return ;
yield return ;
yield return ;
}
}

Note: Yield is not a feature of the .Net runtime. It is just a C# language feature which gets compiled into simple IL code by the C# compiler.

The compiler now generates a inner class with following signature (Reflector + some renaming):

[CompilerGenerated]
private sealed class YieldingEnumerator :
IEnumerable<object>, IEnumerator<object>
{
// Fields
private int state;
private int current;
public YieldingClass owner;
private int initialThreadId; // Methods
[DebuggerHidden]
public YieldingEnumerator(int state);
private bool MoveNext();
[DebuggerHidden]
IEnumerator<int> IEnumerable<int>.GetEnumerator();
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator();
[DebuggerHidden]
void IEnumerator.Reset();
void IDisposable.Dispose(); // Properties
object IEnumerator<object>.Current
{ [DebuggerHidden] get; } object IEnumerator.Current
{ [DebuggerHidden] get; }
}
The original method GetFibonachiSequence() only returns a new instance of the YieldingEnumerator, passing the initial state –2 as well as itself as the owner.

Each enumerator holds a state indicating:

  • -2: Initialized as Enumerable. (Not yet an Enumerator)
  • -1: Closed
  • 0: Initialized as Enumerator.  
    If a new Enumerator is requested on the same instance, GetEnumerator() returns another new instance of YieldingEnumerator.
  • 1-n: Index of the yield return in the original GetFibonachiSequence()method. In case of nested enumerators or other more complex scenarios one yield return consumes more than one index.

The content of GetFibonachiSequence() is translated into YieldingEnumerator.MoveNext().

In our very simple scenario the code looks like this:

bool MoveNext()
{
switch (state)
{
case :
state = -;
current = ;
state = ;
return true; case :
state = -;
current = ;
state = ;
return true; case :
state = -;
current = ;
state = ;
return true; case :
state = -;
current = ;
state = ;
return true; case :
state = -;
break;
}
return false;
}
Quite easy, isn’t it?

So far we easily could have created the classes and methods used to enable the yield keyword ourselves, too.

But in more complex scenarios Microsoft does some tricks, which won’t compile as C# – at least not how Reflector translates the resulting IL code.

Lets have a look at some code with a nested enumeration…

foreach(int i in new int[] {, , , , })
{
yield return i;
}
This compiles into:
private bool MoveNext()
{
try
{
switch (state)
{
case 0:
state = -1;
state = 1;
this.values = new int[] { 1, 2, 3, 5, 8 };
this.currentPositionInValues = 0;
while (this.currentPositionInValues < this.values.Length)
{
current_i = this.values[this.currentPositionInValues];
current = current_i;
state = 2;
return true;
Label_007F:
state = 1;
this.currentPositionInValues++;
}
this.Finally2();
break; case 2:
goto Label_007F;
}
return false;
}
fault
{
this.System.IDisposable.Dispose();
}
}
[/sourcecode]

Now the states 1 and 2 are used to indicate whether the enumerator actually is at some point (2), or wether it is trying to retrieve the next value (1).

Two things would not compile:

  • goto Label_007F is used to jump back into the iteration over int[] values. The C# goto statement is not able to jump into another statements context. But in IL this is totally valid as a while statement in MSIL is nothing but some gotos either.
  • The fault is proper MSIL, but not supported in C#. Basically it acts as a finally which just is executed in case of an error.

Attention: As in anonymous delegates, parameters as well as local and instance variables are passed to the YieldingEnumerator only once. Read this great post on this: Variable Scoping in Anonymous Delegates in C#

Thanks for your attention!

Behind the scenes of the C# yield keyword(转)的更多相关文章

  1. What is the use of c# “Yield” keyword ?

    What is the use of c# “Yield” keyword ? “Yield keyword helps us to do custom stateful iteration over ...

  2. What is the yield keyword used for in C#?

    What is the yield keyword used for in C#? https://stackoverflow.com/a/39496/3782855 The yield keywor ...

  3. 【转向Javascript系列】深入理解Generators

    随着Javascript语言的发展,ES6规范为我们带来了许多新的内容,其中生成器Generators是一项重要的特性.利用这一特性,我们可以简化迭代器的创建,更加令人兴奋的,是Generators允 ...

  4. .NET中的yield关键字

    浅谈yield http://www.cnblogs.com/qlb5626267/archive/2009/05/08/1452517.html .NET中yield关键字的用法 http://bl ...

  5. Python关键字yield详解以及Iterable 和Iterator区别

    迭代器(Iterator) 为了理解yield是什么,首先要明白生成器(generator)是什么,在讲生成器之前先说说迭代器(iterator),当创建一个列表(list)时,你可以逐个的读取每一项 ...

  6. [Python学习笔记-005] 理解yield

    网络上介绍yield的文章很多,但大多讲得过于复杂或者追求全面以至于反而不好理解.本文用一个极简的例子给出参考资料[1]中的讲解,因为个人觉得其讲解最为通俗易懂,读者只需要对Python的列表有所了解 ...

  7. Python yield解析

    Pyhton generators and the yield keyword At a glance,the yield statement is used to define generators ...

  8. yield return,yield break

    转自, http://www.cnblogs.com/kingcat/archive/2012/07/11/2585943.html yield return 表示在迭代中下一个迭代时返回的数据,除此 ...

  9. Understanding Asynchronous IO With Python 3.4's Asyncio And Node.js

    [转自]http://sahandsaba.com/understanding-asyncio-node-js-python-3-4.html Introduction I spent this su ...

随机推荐

  1. win10 家庭版使用注册表关闭windows defender

    管理员身份运行 reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows Defender" /v " ...

  2. Storm+kafka的HelloWorld初体验

    从16年4月5号开始学习kafka,后来由于项目需要又涉及到了storm. 经过几天的扫盲,到今天16年4月13日,磕磕碰碰的总算是写了一个kafka+storm的HelloWorld的例子. 为了达 ...

  3. CSS实现input默认文字灰色有提示文字点击后消失鼠标移开显示

    CSS实现input美化操作默认是为灰色,并且有提示 如下图 鼠标点击后文字消失,鼠标移开后文字显示 给input入下图添加代码 style="color:#cccccc; outline: ...

  4. ADT

    1.什么是antant是构建工具2.什么是构建概念到处可查到,形象来说,你要把代码从某个地方拿来,编译,再拷贝到某个地方去等等操作,当然不仅与此,但是主要用来干这个3.ant的好处跨平台   --因为 ...

  5. 编写高质量代码改善C#程序的157个建议——建议107:区分静态类和单例

    建议107:区分静态类和单例 有一种观点认为:静态类可以作为单件模式的一种实现方式.事实上,这是不妥当的.按照传统的观点来看,单例是一个实例对象.而静态类并不满足这一点.静态类也直接违反面向对象三大特 ...

  6. win32 zbar

    一.zbar官方介绍 ZBar 是款桌面电脑用条形码/二维码扫描工具,支持摄像头及图片扫描,支持多平台,例如 iPhone,Andriod 手机,同时 ZBar封装了二维码扫描的 API 开发包. Z ...

  7. 使用JavaScript获取CSS伪元素属性

    我们可以通过DOM元素的 style 属性获取基本的CSS样式值, 但怎么获取CSS伪元素属性呢? 如下 // 获取 .element:before 的 color 值 var color = win ...

  8. 微信小程序自定义事件

    案例结构 首先,我还是会以案例的形式向大家讲解(这样也能方便大家更好的理解)简单介绍一下案例项目的内容(以上一章自定义组件的案例为基础)项目名称:component自定义子组件cpt父组件:logs ...

  9. Android-SDCard外部存储文件读写

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses ...

  10. C#操作系统计划任务

    首先需要引用system32下的taskschd.dll 测试环境:win8+vs2012+.net4.0 /// <summary> /// 删除任务 /// </summary& ...