The implementation of iterators in C# and its consequences (part 1) Raymond Chen
Likeanonymous methods,
iterators in C# are very complex syntactic sugar.
You could do it all yourself (after all, you did have to do
it all yourself in earlier versions of C#),
but the compiler transformation makes for much greater convenience.
The idea behind iterators is that they take a function withyield return
statements
(and possible some yield break
statements)
and convert it into a state machine.
When you yield return
, the state of the function is
recorded, and execution resumes from that state the next time the
iterator is called upon to produce another object.
Here’s the basic idea:
All the local variables of the iterator (treating iterator parameters
as pre-initialized local variables, including the hidden this
parameter)
become member variables of a helper class.
The helper class also has an internal state member that keeps
track of where execution left off and an internal current
member that holds the object most recently enumerated.
class MyClass { int limit = ; public MyClass(int limit) { this.limit = limit; } public IEnumerable<int> CountFrom(int start) { for (int i = start; i <= limit; i++) { yield return i; } } }
The CountFrom
method produces an integer
enumerator that spits out the integers starting at start
and continuing up to and including limit
.
The compiler internally converts this enumerator into
something like this:
class MyClass_Enumerator : IEnumerable<int> { int state$ = ;// internal member int current$; // internal member MyClass this$; // implicit parameter to CountFrom int start; // explicit parameter to CountFrom int i; // local variable of CountFrom public int Current { get { return current$; } } public bool MoveNext() { switch (state$) { case : goto resume$; case : goto resume$; case : return false; } resume$:; for (i = start; i <= this$.limit; i++) { current$ = i; state$ = ; return true; resume$:; } state$ = ; return false; } … other bookkeeping, not important here … } public IEnumerable<int> CountFrom(int start) { MyClass_Enumerator e = new MyClass_Enumerator(); e.this$ = this; e.start = start; return e; }
用dnSpy反编译上面的代码,同时在配置中
得到如下代码,是一个状态机
// Token: 0x02000005 RID: 5
internal class MyClass
{
// Token: 0x06000006 RID: 6 RVA: 0x000020C9 File Offset: 0x000002C9
public MyClass(int limit)
{
this.limit = limit;
} // Token: 0x06000007 RID: 7 RVA: 0x000020E1 File Offset: 0x000002E1
public IEnumerable<int> CountFrom(int start)
{
MyClass.<CountFrom>d__2 <CountFrom>d__ = new MyClass.<CountFrom>d__2(-);
<CountFrom>d__.<>4__this = this;
<CountFrom>d__.<>3__start = start;
return <CountFrom>d__;
} // Token: 0x04000001 RID: 1
private int limit = ; // Token: 0x02000006 RID: 6
[CompilerGenerated]
private sealed class <CountFrom>d__2 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IDisposable, IEnumerator
{
// Token: 0x06000008 RID: 8 RVA: 0x000020F8 File Offset: 0x000002F8
[DebuggerHidden]
public <CountFrom>d__2(int <>1__state)
{
this.<>1__state = <>1__state;
this.<>l__initialThreadId = Environment.CurrentManagedThreadId;
} // Token: 0x06000009 RID: 9 RVA: 0x00002113 File Offset: 0x00000313
[DebuggerHidden]
void IDisposable.Dispose()
{
} // Token: 0x0600000A RID: 10 RVA: 0x00002118 File Offset: 0x00000318
bool IEnumerator.MoveNext()
{
int num = this.<>1__state;
if (num != )
{
if (num != )
{
return false;
}
this.<>1__state = -;
int num2 = this.<i>5__1;
this.<i>5__1 = num2 + ;
}
else
{
this.<>1__state = -;
this.<i>5__1 = this.start;
}
if (this.<i>5__1 > this.<>4__this.limit)
{
return false;
}
this.<>2__current = this.<i>5__1;
this.<>1__state = ;
return true;
} // Token: 0x17000001 RID: 1
// (get) Token: 0x0600000B RID: 11 RVA: 0x0000219C File Offset: 0x0000039C
int IEnumerator<int>.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
} // Token: 0x0600000C RID: 12 RVA: 0x000021A4 File Offset: 0x000003A4
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
} // Token: 0x17000002 RID: 2
// (get) Token: 0x0600000D RID: 13 RVA: 0x000021AB File Offset: 0x000003AB
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return this.<>2__current;
}
} // Token: 0x0600000E RID: 14 RVA: 0x000021B8 File Offset: 0x000003B8
[DebuggerHidden]
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
MyClass.<CountFrom>d__2 <CountFrom>d__;
if (this.<>1__state == - && this.<>l__initialThreadId == Environment.CurrentManagedThreadId)
{
this.<>1__state = ;
<CountFrom>d__ = this;
}
else
{
<CountFrom>d__ = new MyClass.<CountFrom>d__2();
<CountFrom>d__.<>4__this = this.<>4__this;
}
<CountFrom>d__.start = this.<>3__start;
return <CountFrom>d__;
} // Token: 0x0600000F RID: 15 RVA: 0x00002207 File Offset: 0x00000407
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return this.System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator();
} // Token: 0x04000002 RID: 2
private int <>1__state; // Token: 0x04000003 RID: 3
private int <>2__current; // Token: 0x04000004 RID: 4
private int <>l__initialThreadId; // Token: 0x04000005 RID: 5
private int start; // Token: 0x04000006 RID: 6
public int <>3__start; // Token: 0x04000007 RID: 7
public MyClass <>4__this; // Token: 0x04000008 RID: 8
private int <i>5__1;
}
}
The enumerator class is auto-generated by the compiler
and, as promised, it contains two internal members for the
state and current object,
plus a member for each parameter
(including the hidden this
parameter),
plus a member for each local variable.
The Current
property merely returns the current object.
All the real work happens in MoveNext
.
To generate the MoveNext
method, the compiler
takes the code you write and performs a few transformations.
First, all the references to variables and parameters need to
be adjusted since the code moved to a helper class.
Notice that this transformation is quite different fromthe enumeration model we built based on coroutines and fibers.
The C# method is far more efficient in terms of memory usage
since it doesn’t consume an entire stack (typically a megabyte in size)
like the fiber approach does.
Instead it just borrows the stack of the caller,
and anything that it needs to save across calls to MoveNext
are stored in a helper object (which goes on the heap rather than the stack).
This fake-out is normally quite effective—most
people don’t even realize that it’s happening—but there are places
where the difference is significant, and we’ll see that shortly.
The implementation of iterators in C# and its consequences (part 1) Raymond Chen的更多相关文章
- 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 ...
- 一次C#和C++的实际应用性能比较(C++允许我们使用任何手段来提高效率,只要愿意做出足够的努力)
05年时,在微软的Rico Mariani做了一次实际应用的C#和C++的性能比较.事情起源于微软著名的元老Raymond Chen(在下敬仰的超级牛人)用C++写了一个英汉词典程序,来描述讲解优化C ...
- cvpr2015papers
@http://www-cs-faculty.stanford.edu/people/karpathy/cvpr2015papers/ CVPR 2015 papers (in nicer forma ...
- Python 的上下文管理器是怎么设计的?
花下猫语:最近,我在看 Python 3.10 版本的更新内容时,发现有一个关于上下文管理器的小更新,然后,突然发现上下文管理器的设计 PEP 竟然还没人翻译过!于是,我断断续续花了两周时间,终于把这 ...
- Implementation with Java
Implementation with Java From:http://jcsc.sourceforge.net In general, follow the Sun coding conventi ...
- Python标准模块--Iterators和Generators
1 模块简介 当你开始使用Python编程时,你或许已经使用了iterators(迭代器)和generators(生成器),你当时可能并没有意识到.在本篇博文中,我们将会学习迭代器和生成器是什么.当然 ...
- Design and Implementation of the Sun Network File System
Introduction The network file system(NFS) is a client/service application that provides shared file ...
- [转]Objective-c中@interface、@implementation、@protocal
原处:http://blog.csdn.net/l271640625/article/details/8393531 以下Objective-c简称OC 从事java开发的程序员们都知道,在java中 ...
- Implementation Model Editor of AVEVA in OpenSceneGraph
Implementation Model Editor of AVEVA in OpenSceneGraph eryar@163.com 摘要Abstract:本文主要对工厂和海工设计软件AVEVA的 ...
随机推荐
- 分布式Hadoop和HBase整合
环境: CentOS 6.6 hadoop-2.6.0 hbase-0.98.10.1 HBase1 192.168.1.4 Mast ...
- PAT基础级-钻石段位样卷2-7-2 吃鱼还是吃肉 (10 分)
国家给出了 8 岁男宝宝的标准身高为 130 厘米.标准体重为 27 公斤:8 岁女宝宝的标准身高为 129 厘米.标准体重为 25 公斤. 现在你要根据小宝宝的身高体重,给出补充营养的建议. 输 ...
- Spring Boot全局异常处理
本文为大家分享了Spring Boot全局异常处理,供大家参考,具体内容如下 1.后台处理异常 a.引入thymeleaf依赖 <!-- thymeleaf模板插件 --> <dep ...
- 2019-ACM-ICPC-南京区网络赛-E. K Sum-杜教筛+欧拉定理
2019-ACM-ICPC-南京区网络赛-E. K Sum-杜教筛+欧拉定理 [Problem Description] 令\(f_n(k)=\sum_{l_1=1}^n\sum_{l_2=1}^n\ ...
- 2018 ACMICPC上海大都会赛重现赛 H - A Simple Problem with Integers (线段树,循环节)
2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 H - A Simple Problem with Integers (线段树,循环节) 链接:https://ac.nowcoder.co ...
- webpack搭建组件库相关知识
1 .inquirer.js —— 一个用户与命令行交互的工具 2. existsSync 方法说明: 以同步的方法检测目录是否存在. 如果目录存在 返回 true ,如果目录不存在 返回false语 ...
- flask中使用ajax 处理前端请求,每隔一段时间请求一次
需求: flask中使用ajax 处理前端请求,每隔一段时间请求一次,并展示在页面 使用 setInterval(function(){},1000)方法 结果展示: html:(test.html) ...
- java中的volatile变量
同步与线程间通信: 通信 通信是指消息在两条线程之间传递. 既然要传递消息,那接收线程 和 发送线程之间必须要有个先后关系,此时就需要用到同步.通信和同步是相辅相成的. 同步 同步是指,控制多条线程之 ...
- 执行Commit时Oracle做哪些工作
COMMIT是一个非常快的操作,当我们发布commit命令时,真正困难的动作已经完成,在数据库中已经执行了数据更改,所以已经完成了99%的任务,例如:下列操作已经产生: 1.在SGA(Buffer C ...
- qsort中的函数指针,及函数解释
函数指针有何用 函数指针的应用场景比较多,以库函数qsort排序函数为例,它的原型如下: void qsort(void *base,size_t nmemb,size_t size , int(*c ...