C# 收益表实现
我想以易于理解的形式了解C# 收益表实现,c#,.net,yield,iterator,C#,.net,Yield,Iterator,我想以易于理解的形式了解yield语句的所有内容 我已经阅读了yield语句,以及它在实现迭代器模式时的易用性。然而,大部分是非常干燥的。我想了解一下微软是如何处理收益率的 另外,什么时候使用yield break?yield通过在内部构建状态机来工作。当例程退出并下次从该状态恢复时,它存储例程的当前状态 您可以使用Reflector查看编译器如何实现它 当您想要停止返回结果时,将使用产量中断。如果没有屈服中断,编译器将在函数末尾假设一个屈服中断(就像正常函数中的返回;语句)正如Mehrdad所
yield
语句的所有内容
我已经阅读了yield
语句,以及它在实现迭代器模式时的易用性。然而,大部分是非常干燥的。我想了解一下微软是如何处理收益率的
另外,什么时候使用yield break?
yield
通过在内部构建状态机来工作。当例程退出并下次从该状态恢复时,它存储例程的当前状态
您可以使用Reflector查看编译器如何实现它
当您想要停止返回结果时,将使用
产量中断
。如果没有屈服中断
,编译器将在函数末尾假设一个屈服中断(就像正常函数中的返回;
语句)正如Mehrdad所说,它构建了一个状态机
以及使用反射器(另一个很好的建议),你可能会发现有用的。如果不是
finally
块,它将相对简单,但它们引入了一个额外的复杂度维度 让我们倒带一点:yield
关键字被翻译成许多其他人所说的状态机
实际上,这并不完全像是使用一个内置的实现,该实现将在后台使用,而是编译器通过实现一个相关接口(包含yield
关键字的方法的返回类型)将yield
相关代码重写到状态机
(有限)只是一段代码,它根据您在代码中的位置(取决于之前的状态,输入)转到另一个状态操作,当您使用方法返回类型为IEnumerator
/IEnumerator
时,几乎就是这样。yield
关键字将创建另一个操作以从上一个操作移动到下一个状态,因此在MoveNext()
实现中创建状态管理
这正是C#编译器/Roslyn要做的事情:检查yield
关键字的存在以及包含方法的返回类型,无论它是IEnumerator
,IEnumerable
,IEnumerable
还是IEnumerable
,然后创建反映该方法的私有类,整合必要的变量和状态
如果您对状态机以及编译器如何重写迭代的细节感兴趣,可以在Github上查看这些链接:
异步/等待代码时使用的(代码)也继承自状态机编写器
,因为它还利用了后面的状态机
如前所述,状态机在bool MoveNext()
生成的实现中得到了很好的反映,其中有一个开关
+有时一些老式的转到
,它基于状态字段,表示方法中不同状态的不同执行路径
编译器从用户代码生成的代码看起来不太“好”,主要是因为编译器在这里和那里添加了一些奇怪的前缀和后缀
例如,代码:
public class TestClass
{
private int _iAmAHere = 0;
public IEnumerator<int> DoSomething()
{
var start = 1;
var stop = 42;
var breakCondition = 34;
var exceptionCondition = 41;
var multiplier = 2;
// Rest of the code... with some yield keywords somewhere below...
正如您所看到的,这个实现远非简单明了,但它确实做到了
琐事2:使用IEnumerable
/IEnumerable
方法返回类型会发生什么情况?
好的,它不只是生成一个实现IEnumerator
的类,而是生成一个同时实现IEnumerable
和IEnumerator
的类,这样IEnumerator GetEnumerator()
的实现将利用相同生成的类
使用yield
关键字时自动实现的几个接口的温馨提示:
public interface IEnumerable<out T> : IEnumerable
{
new IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<out T> : IDisposable, IEnumerator
{
T Current { get; }
}
public interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
}
公共接口IEnumerable:IEnumerable
{
新的IEnumerator GetEnumerator();
}
公共接口IEnumerator:IDisposable,IEnumerator
{
T当前{get;}
}
公共接口IEnumerator
{
bool-MoveNext();
当前对象{get;}
无效重置();
}
您还可以使用不同的路径/分支以及编译器重写的完整实现进行检查
这是用创建的,您可以使用该工具尝试不同的产生
相关执行路径,并查看编译器将如何在MoveNext
实现中将它们重写为状态机
关于问题的第二部分,即,收益率突破
,已经回答了
它指定迭代器已经结束
将break作为不返回值的返回语句生成
“例程的当前状态”是什么意思:处理器寄存器值、帧指针等?请看@Tcraft Microsoft的规范化实现没有使用不同的堆栈/分段堆栈等。它们使用堆分配的对象来存储状态。
public class Example
{
public IEnumerator<string> DoSomething()
{
const int start = 1;
const int stop = 42;
for (var index = start; index < stop; index++)
{
yield return index % 2 == 0 ? "even" : "odd";
}
}
}
private bool MoveNext()
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<start>5__1 = 1;
<stop>5__2 = 42;
<index>5__3 = <start>5__1;
break;
case 1:
<>1__state = -1;
goto IL_0094;
case 2:
{
<>1__state = -1;
goto IL_0094;
}
IL_0094:
<index>5__3++;
break;
}
if (<index>5__3 < <stop>5__2)
{
if (<index>5__3 % 2 == 0)
{
<>2__current = "even";
<>1__state = 1;
return true;
}
<>2__current = "odd";
<>1__state = 2;
return true;
}
return false;
}
public interface IEnumerable<out T> : IEnumerable
{
new IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<out T> : IDisposable, IEnumerator
{
T Current { get; }
}
public interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
}