收益率工作模式
当我有一个代码块时,
static void Main()
{
foreach (int i in YieldDemo.SupplyIntegers())
{
Console.WriteLine("{0} is consumed by foreach iteration", i);
}
}
class YieldDemo
{
public static IEnumerable<int> SupplyIntegers()
{
yield return 1;
yield return 2;
yield return 3;
}
}
我可以将yield return背后的原理解释为
- Main()调用SupplyIntegers()
|1| |2| |3|存储在连续的内存块中。“IEnumerator”的指针移动到 |1|
- 控制从 SupplyInteger() 返回到 Main()。
- Main() 打印值
- Pointer Moves to |2|,依此类推。
说明:
(1) 通常我们会在函数内部允许有一个有效的 return 语句。 当存在多个yield return、yield return、...语句时,C# 会如何处理?
(2) 一旦遇到返回,控制就无法再次返回 SupplyIntegers(),如果允许,Yield 不会再次从 1 开始吗?我的意思是收益率1?
When i have a code block
static void Main()
{
foreach (int i in YieldDemo.SupplyIntegers())
{
Console.WriteLine("{0} is consumed by foreach iteration", i);
}
}
class YieldDemo
{
public static IEnumerable<int> SupplyIntegers()
{
yield return 1;
yield return 2;
yield return 3;
}
}
can i interpret the principle behind yield return as
- Main() calls the SupplyIntegers()
|1| |2| |3| are stored in contiguous memory block.Pointer of "IEnumerator" Moves to |1|
- Control returns from SupplyInteger() to Main().
- Main() prints the value
- Pointer Moves to |2|, and so on.
Clarifications :
(1) Normally we will have one valid return statement is allowed inside a function.How does
C# treats when multiple yield return ,yield return,... statements are present?
(2) Once the return is encountered there is no way for control again coming back to
SupplyIntegers(), in case it is allowed won't the Yield again starts from 1 ? I mean yield return 1?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
不——远非如此;我给你写一个长手写的版本……太蹩脚了!
请注意,如果您了解
foreach
实际上是有帮助的:如您所见,它在迭代器中构建一个状态机,状态机通过
MoveNext
进行。我使用了带有state
字段的模式,您可以看到这对于更复杂的迭代器是如何工作的。重要的是:
finally
块(包括using
),迭代器块中的任何变量都会成为状态机上的字段Dispose()
yield return
的case
(粗略地)yield break
变成 <代码>状态=-1; return false; (或类似)C# 编译器执行此操作的方式非常复杂,但它使编写迭代器变得轻而易举。
Nope - far from it; I'll write a long-hand version for you... it is too grungy!
Note it also helps if you understand that the
foreach
is actually:As you can see, it builds a state machine in the iterator, with the state machine progressed by
MoveNext
. I've used the pattern with astate
field, as you can see how this would work for more complex iterators.Importantly:
finally
block (includingusing
), it goes in theDispose()
yield return
become acase
(roughly)yield break
becomes astate = -1; return false;
(or similar)The way the C# compiler does this is very complicated, but it makes writing iterators a breeze.
它只是一个语法糖,.net 为您生成 IEnumerator 类并实现 MoveNext、Current 和 Reset 方法,然后生成 IEnumarable 类 GetEnumerator,其中返回 IEnumerator,您可以通过 .net Reflector 或 ildasm 看到这些魔术类。
另请参阅此处
It's just a syntax sugar, .net generates IEnumerator class for you and implements MoveNext, Current and Reset methods, than generates IEnumarable class GetEnumerator of which returns that IEnumerator, you can see that magic classes by .net reflector or ildasm.
Also see here
简而言之,迭代器块(或者带有
yield
语句的方法,如果可以的话)被编译器转换为编译器生成的类。此类实现IEnumerator
且yield
语句转换为该类的“状态”。例如,这:
可能会转换成类似于:
迭代器块可以被视为抽象状态机。此代码将由
IEnumerator
的方法调用。Simply put, iterator blocks (or methods with
yield
statements, if you may) are transformed by the compiler into a compiler-generated class. This class implementsIEnumerator
and theyield
statement is transformed into a 'state' for that class.For instance, this:
might get transformed into something similar to:
Iterator blocks are can be seen as abstracted state machines. This code will be invoked by
IEnumerator
's methods.简而言之,(当您等待 marc 的长手版本时)当编译器看到yield 语句时,它会在幕后为您构建一个自定义类的新实例,该实例实现一个名为
IEnumerator
的接口,该实例具有方法Current()
和MoveNext()
,并跟踪您当前在迭代过程中的位置...在上面的示例中,它将还跟踪要枚举的列表中的值。in short, (while yr waiting for marc's long-hand version) when the compiler sees yield statements, behind the scenes it builds a new instance of a custom class for you that implements an interface called
IEnumerator
, which has methodsCurrent()
, andMoveNext()
, and keeps track of where you are currently in the iteration process... In the case above as your example, it would also keep track of the values in the list to be enumerated through.