分类
标签
.NET 9008 adb android apt asp.net ASP.NET Core audio bug C c++ C++ chrome cmd csharp CSharp css css3 debian debug dotnet dotnet Framework dpkg GDI&GDI+ gdi&gdi+ golang graphics html html5 http java javascript json kali linux linux mci microsoft minimap MSBuild mysql OpenCV PInvoke powershell python QQ rust shell speex sql tutorial ubuntu ui unity vb.net visual studio Visual Studio web Web win32 winapi windows winform WinForm wpf WPF xaml xfce 列表 刷机 前端 加密 反射 反编译 可视化 图像处理 多线程 字符串 安卓 实例 局域网 幻影坦克 库 开发语言 异步 微信 手册 手机 接口 摘要 救砖 数字签名 数字证书 数字音频 数据库 桌面程序 游戏 游戏引擎 源码 爬虫 玩游戏 电脑硬件 笔记 算法 类库 线性代数 编程语言 网络 脚本语言 计算机图形学 计算机基础 设计模式 语音编解码 运维 进制 面向对象编程 音频 音频编码解码
551 字
3 分钟
[C#] 基于 yield 语句的迭代器逻辑懒执行
众所周知, C# 可以通过 yield
语句来快速向 IEnumerator
或者 IEnumerable
类型的方法返回值返回一个元素. 但它还有另外一个特性, 就是其内部逻辑的懒执行. 每两个 yield
语句之间的逻辑都是一个状态, 只有在调用迭代器的 MoveNext
方法后, 才会执行下一个状态的逻辑.
在文章中, 编译后的代码已经经过简化和删减, 以便于理解
迭代器方法的懒执行
举一个简单的例子:
IEnumerator SomeLogic()
{
Console.WriteLine("hello world");
yield return null;
Console.WriteLine("fuck you world");
}
在调用的时候, 逻辑并不会被立即执行, 只有对其返回的迭代器调用 MoveNext
的时候, 才会继续执行.
var enumerator = SomeLogic();
enumerator.MoveNext(); // 打印 hello world
enumerator.MoveNext(); // 打印 fuck you world
迭代器方法的编译
在 C# 中, 使用了 yield 语句的, 返回 IEnumerator 或 IEnumerable 的方法, 会由编译器生成一个迭代器或可迭代类型, 在类型的内部包含该方法的逻辑.
例如上面提到的代码, 它会被编译成大概这样:
IEnumerator SomeLogic()
{
return new SomeLogicEnumerator();
}
private sealed class SomeLogicEnumerator : IEnumerator, IDisposable
{
private int state;
private object current;
object IEnumerator.Current
{
get
{
return current;
}
}
public SomeLogicEnumerator(int state)
{
this.state = state;
}
void IDisposable.Dispose()
{
}
private bool MoveNext()
{
int num = state;
if (num != 0)
{
if (num != 1)
{
return false;
}
state = -1;
Console.WriteLine("AWA");
return false;
}
state = -1;
Console.WriteLine("QWQ");
current = null;
state = 1;
return true;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
我们可以看到, 在这个迭代器类型中, 有一个 state
字段存储了当前的状态, 而在 MoveNext
被调用时, 会切换当前状态, 然后根据当前状态执行对应逻辑.
当然, 如果你的迭代器逻辑稍微长一些, 它也是可以处理的.
IEnumerator SomeLogic()
{
Console.WriteLine("QWQ");
yield return 1;
Console.WriteLine("AWA");
yield return 2;
Console.WriteLine("QJFD");
yield return 3;
Console.WriteLine("JWOEIJFOIWE");
}
它的 MoveNext
就变成了这样:
private bool MoveNext()
{
switch (state)
{
case 0:
Console.WriteLine("QWQ");
current = 1;
state = 1;
return true;
case 1:
Console.WriteLine("AWA");
current = 2;
state = 2;
return true;
case 2:
Console.WriteLine("QJFD");
current = 3;
state = 3;
return true;
case 3:
state = -1;
Console.WriteLine("JWOEIJFOIWE");
return false;
default:
return false;
}
}
[C#] 基于 yield 语句的迭代器逻辑懒执行
https://slimenull.com/posts/20231208192209/