解读 --- yield 关键字
引言
yield关键字是 C# 中的一种语言特性,用于在枚举器中简化迭代器的实现。它使得开发人员可以通过定义自己的迭代器来简化代码,而不必手动实现 IEnumerable 和 IEnumerator 接口。
使用 yield 关键字,可以将迭代器中的值一次一个地返回,而不必使用一个集合对象存储所有的值。当执行到yield return语句时,代码将会暂停执行,将返回值传递给迭代器的调用者,并将迭代器的状态保存下来。当下一次调用MoveNext方法时,代码将从之前的暂停点继续执行,直到遇到下一个yield return语句或者迭代器结束。
接下来探索一下 yield 的三种玩法:
初级
例如通过 yield 创建出一个 IEnumerable 以供 foreach 遍历,代码如下:
internal class Program
{
static void Main(string[] args)
{
foreach (int i in EvenSequence(5, 18))
{
Console.Write(i + " ");
}
Console.ReadKey();
}
static IEnumerable<int> EvenSequence(int start, int end)
{
for (int i = start; i <= end; i++)
{
if (i % 2 == 0)
{
yield return i;
}
}
}
}
在上面的代码中,我们定义了一个名为EvenSequence的方法,它返回一个实现了IEnumerable<int>接口的对象。在EvenSequence方法中,我们使用yield return语句来返回每个偶数值,并在每次暂停后保存方法的状态。
在Main方法中,我们使用foreach循环语句来遍历EvenSequence方法返回的集合对象,并输出每个偶数值。由于我们使用了 yield 关键字,即使我们没有显式地实现IEnumerable和IEnumerator接口,也能够遍历集合对象。
进阶
另一个方面,异步编程中也常常使用yield来创建异步生成器。使用yield创建异步生成器可以让我们轻松地以异步方式生成一系列值,而无需显式地管理异步状态。如以下代码:
public class Program
{
public static void Main(string[] args)
{
AsynchronousIterate();
Console.ReadLine();
}
public static async void AsynchronousIterate()
{
await foreach (var number in GenerateNumbersAsync())
{
Console.WriteLine(number);
}
}
public static async IAsyncEnumerable<int> GenerateNumbersAsync()
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(1000);
yield return i;
}
}
}
上述代码定义了一个名为 GenerateNumbersAsync 的异步方法,该方法返回一个 IAsyncEnumerable<int> 类型的对象。在方法体中,我们使用一个 for 循环来生成一系列整数,并在每次迭代中异步等待1秒钟。紧接着,我们使用 yield return 语句将生成的整数返回给调用方。 要注意调用时使用异步迭代器(具有 await 关键字的foreach)来进行遍历>。
上述输出则是一秒输出一个结果,知道全部输出。
进阶举例场景
现在有这一样一个场景,有一大缸水,你手里有一个水瓢,,现在需要你把水缸里的水,全部移到另一个水缸。
前提条件:现在不知道缸里由具体多少水,也无法确定一瓢能舀多少,也无法确定你每一次舀水操作需要多长时间。
现在写一段代码,模拟这个过程。
拟定前提条件,如下代码:
static Random Random = new Random();
/// <summary>
/// 假设水缸能盛水90~100
/// </summary>
public static int GetOneWaterTankCapacity()
{
return Random.Next(150, 200);
}
/// <summary>
/// 假设水瓢一次只能舀水2~5
/// </summary>
public static int GetOneWaterLadleCapacity()
{
return Random.Next(2, 5);
}
/// <summary>
/// 模拟舀水所消耗的时间(0.5s~2S)
/// </summary>
/// <returns></returns>
public static async Task<int> ScoopingWater()
{
await Task.Delay(Random.Next(500, 2000));
return GetOneWaterLadleCapacity();
}
开始舀水,代码如下:
/// <summary>
/// 开始搬水
/// </summary>
/// <returns></returns>
public static async void MoveWater()
{
//先确定缸里有多少水
var WaterTankCapacity = GetOneWaterTankCapacity();
await foreach (var item in CreateTasks(WaterTankCapacity))
{
Console.WriteLine($"这一瓢舀水量:{item.Result}\t{DateTime.Now}");
}
Console.WriteLine($"水全部舀完~\t{DateTime.Now}");
}
private static async IAsyncEnumerable<Task<int>> CreateTasks(int waterTankCapacity)
{
int totle = 0;
while (totle < waterTankCapacity)
{
Task<int> someWater = ScoopingWater();
yield return someWater;
totle += await someWater;
}
}
最终会每次随机舀水,花费随机时间,水全部舀完。
输出:
这一瓢舀水量:4 2023/5/11 23:06:29
这一瓢舀水量:3 2023/5/11 23:06:30
这一瓢舀水量:2 2023/5/11 23:06:32
这一瓢舀水量:2 2023/5/11 23:06:34
这一瓢舀水量:3 2023/5/11 23:06:35
这一瓢舀水量:4 2023/5/11 23:06:36
这一瓢舀水量:2 2023/5/11 23:06:37
....
水全部舀完~ 2023/5/11 23:07:27
通过上文例子,可以更深入理解 yield 创建的异步生成器。
总结
- 使用
yield关键字可以将一个方法转换为一个返回可枚举对象或迭代器的方法,而不必手动实现 IEnumerable 和 IEnumerator 接口 - 使用
yield来创建异步生成器,在某些场景下可以实现更高效、可靠的异步编程。
解读 --- yield 关键字的更多相关文章
- 从yield关键字看IEnumerable和Collection的区别
C#的yield关键字由来以久,如果我没有记错的话,应该是在C# 2.0中被引入的.相信大家此关键字的用法已经了然于胸,很多人也了解yield背后的“延迟赋值”机制.但是即使你知道这个机制,你也很容易 ...
- .NET中的yield关键字
浅谈yield http://www.cnblogs.com/qlb5626267/archive/2009/05/08/1452517.html .NET中yield关键字的用法 http://bl ...
- 使用yield关键字让自定义集合实现foreach遍历
一般来说当我们创建自定义集合的时候为了让其能支持foreach遍历,就只能让其实现IEnumerable接口(可能还要实现IEnumerator接口) 但是我们也可以通过使用yield关键字构建的迭代 ...
- C#的yield关键字
using System; using System.Collections.Generic; using System.Reflection; using System.Text.RegularEx ...
- 从range和xrange的性能对比到yield关键字(中)
上节提出了range和xrange的效率问题,这节我们来探究其中的原因 yield的使用 我们看下面的程序: #coding: utf-8 def test(): print 4 print ...
- (转) Python Generators(生成器)——yield关键字
http://blog.csdn.net/scelong/article/details/6969276 生成器是这样一个函数,它记住上一次返回时在函数体中的位置.对生成器函数的第二次(或第 n 次) ...
- 转载yield关键字理解
实现IEnumerable接口及理解yield关键字 [摘要]本文介绍实现IEnumerable接口及理解yield关键字,并讨论IEnumerable接口如何使得foreach语句可以使用. 本 ...
- yield关键字的用法
在上一篇文章中,说了下foreach的用法,但是还是比较复杂的,要实现接口才能进行遍历,有没有简单些的方法呢?答案是肯定的.且看下面. yield关键字的用法: 1.为当前类型添加一个任意方法,但是要 ...
- yield 关键字和迭代器
一般使用方法 yield 关键字向编译器指示它所在的方法是迭代器块 在迭代器块中,yield 关键字与 return 关键字结合使用,向枚举器对象提供值. 这是一个返回值,例如,在 forea ...
- C# 基础小知识之yield 关键字 语法糖
原文地址:http://www.cnblogs.com/santian/p/4389675.html 对于yield关键字我们首先看一下msdn的解释: 如果你在语句中使用 yield 关键字,则意味 ...
随机推荐
- Midjourney 提示词工具(10 个国内外最好最推荐的)
Midjourney,是一个革命性的基于人工智能的艺术生成器,可以从被称为提示的简单文本描述中生成令人惊叹的图像.Midjourney已经迅速成为艺术家.设计师和营销人员的首选工具(包括像我这样根本不 ...
- python-pygal
准备写大作业的时候发现了一个绝绝子的python库. 原文:https://blog.damavis.com/en/creating-vector-graphics-with-python/ 官网:h ...
- live555中ts流详细解析
live555中ts流详细解析 该文档主要是对live555源码下testProgs中testMPEG2TransportStreamer服务器端的详细分析.主要分析ts流实现的总体调用流程.(重新整 ...
- 微信小程序搜索不到腾讯服务路线规划插件的解决方法
具体操作如下: 提示:主要内容都是按开发文档来写的 开发文档: 链接: https://lbs.qq.com/miniProgram/plugin/pluginGuide/routePlan 添加插件 ...
- Prism Sample 8 ViewModelLocator
这一例只是说明ViewModelLocator的使用,没有难度,跳过.
- 2023-02-16:两种颜色的球,蓝色和红色,都按1~n编号,共计2n个, 为方便放在一个数组中,红球编号取负,篮球不变,并打乱顺序, 要求同一种颜色的球按编号升序排列,可以进行如下操作: 交换相邻
2023-02-16:两种颜色的球,蓝色和红色,都按1-n编号,共计2n个, 为方便放在一个数组中,红球编号取负,篮球不变,并打乱顺序, 要求同一种颜色的球按编号升序排列,可以进行如下操作: 交换相邻 ...
- 2022-09-18:以下go语言代码输出什么?A:1;B:15;C:panic index out of range;D:doesn’t compile。 package main import
2022-09-18:以下go语言代码输出什么?A:1:B:15:C:panic index out of range:D:doesn't compile. package main import ( ...
- 2022-04-05:golang中go.mod文件,做框架开发需要解析,请问如何解析?
2022-04-05:golang中go.mod文件,做框架开发需要解析,请问如何解析? 答案2022-04-05: go.mod解析可参考这个.做框架开发主要是用到require和replace.具 ...
- Selenium - 浏览器配置(4) - 打开无痕浏览器
Selenium - 浏览器配置 无痕浏览器 开启谷歌浏览器的无痕浏览模式: from selenium import webdriver # 引入浏览器配置 chrome_options = web ...
- 3 分钟利用 FastGPT 和 Laf 将 ChatGPT 接入企业微信
原文链接:https://forum.laf.run/d/556 FastGPT 是一个超级的 ChatGPT 平台项目,功能非常强大: 集成了 ChatGPT.GPT4 和 Claude 可以使用任 ...