解读 --- 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 关键字,则意味 ...
随机推荐
- python-SSTI模板注入
一.python_SSTI模板注入介绍 ssti漏洞成因 ssti服务端模板注入,ssti主要为python的一些框架 jinja2 mako tornado django,PHP框架smarty t ...
- Godot 4.0 文件读取(C#)
搞半天才弄明白Godot文件操作. Godot的文档总是试图让我使用自定义Resource来支持文件操作,但是我只需要读取纯文本. 读取纯文本 读取纯文本的方式如下: //Godot.FileAcce ...
- VS 查看引用的DLL/Nuget包源码时,无法看到注释
一.问题描述 在下面的截图中,我们发现,源码有添加一段注释. 然后通过Nuget包引用,在VS中用Reshaper反编译时,发现没有注释: 原来,DLL是默认不带注释的.即你生成一个DLL,给另一个项 ...
- HMS Core 6.10.0版本发布公告
分析服务 ◆ 事件分析下新增商品订阅分析报告,帮助开发者了解应用内用户付费订阅概况,评估订阅付费价值: ◆ 营销分析.用户质量.转化分析以及过滤器中,新增广告系列/广告任务通过ID进行搜索的功能,通过 ...
- Prism Sample 16-RegionContext
终于发现一个有趣的新知识了. 本例的核心是RegionContext,意思是一个区域的上下文.但与DataContext似乎并不相同. 先看一下整体思路. 在主窗体上只有一个Region: <G ...
- UIOTOS:一款无门槛的前端0代码搭建工具
什么是UIOTOS? UIOTOS中文名称前端大师,是一款基于图形技术的前端0代码工具,支持通过连线和嵌套无门槛来搭建各类复杂的的交互界面,包括后台管理系统.组态数据大屏等,实现跟代码开发媲美的效果. ...
- vue项目使用rem布局刷新页面瞬间元素尺寸由小变大,页面闪现错乱样式
vue项目使用px2remLoader插件,在index.html自定义设置font-size的大小,尤其是在首屏加载的时候,会出现页面各个元素尺寸由小变大的一个过程,很难看 刚开始一直在想是不是因为 ...
- 2022-11-24:小团在地图上放了3个定位装置,想依赖他们进行定位! 地图是一个n*n的棋盘, 有3个定位装置(x1,y1),(x2,y2),(x3,y3),每个值均在[1,n]内。 小团在(a,
2022-11-24:小团在地图上放了3个定位装置,想依赖他们进行定位! 地图是一个n*n的棋盘, 有3个定位装置(x1,y1),(x2,y2),(x3,y3),每个值均在[1,n]内. 小团在(a, ...
- WSGI实现一个WEB服务
- 第一课 使用开发者模式快速入门 Odoo 13
Odoo提供了一个快速应用开发框架,非常适合创建商业应用.这类应用通常用于保留业务记录,增删改查操作.Odoo 不仅简化了这类应用的创建,还提供了看板.日历.图表等视图的丰富组件,用于创建好看的用户界 ...