引言

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 关键字,即使我们没有显式地实现IEnumerableIEnumerator接口,也能够遍历集合对象。

进阶

另一个方面,异步编程中也常常使用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 关键字的更多相关文章

  1. 从yield关键字看IEnumerable和Collection的区别

    C#的yield关键字由来以久,如果我没有记错的话,应该是在C# 2.0中被引入的.相信大家此关键字的用法已经了然于胸,很多人也了解yield背后的“延迟赋值”机制.但是即使你知道这个机制,你也很容易 ...

  2. .NET中的yield关键字

    浅谈yield http://www.cnblogs.com/qlb5626267/archive/2009/05/08/1452517.html .NET中yield关键字的用法 http://bl ...

  3. 使用yield关键字让自定义集合实现foreach遍历

    一般来说当我们创建自定义集合的时候为了让其能支持foreach遍历,就只能让其实现IEnumerable接口(可能还要实现IEnumerator接口) 但是我们也可以通过使用yield关键字构建的迭代 ...

  4. C#的yield关键字

    using System; using System.Collections.Generic; using System.Reflection; using System.Text.RegularEx ...

  5. 从range和xrange的性能对比到yield关键字(中)

    上节提出了range和xrange的效率问题,这节我们来探究其中的原因   yield的使用   我们看下面的程序: #coding: utf-8 def test(): print 4 print ...

  6. (转) Python Generators(生成器)——yield关键字

    http://blog.csdn.net/scelong/article/details/6969276 生成器是这样一个函数,它记住上一次返回时在函数体中的位置.对生成器函数的第二次(或第 n 次) ...

  7. 转载yield关键字理解

    实现IEnumerable接口及理解yield关键字   [摘要]本文介绍实现IEnumerable接口及理解yield关键字,并讨论IEnumerable接口如何使得foreach语句可以使用. 本 ...

  8. yield关键字的用法

    在上一篇文章中,说了下foreach的用法,但是还是比较复杂的,要实现接口才能进行遍历,有没有简单些的方法呢?答案是肯定的.且看下面. yield关键字的用法: 1.为当前类型添加一个任意方法,但是要 ...

  9. yield 关键字和迭代器

    一般使用方法     yield 关键字向编译器指示它所在的方法是迭代器块 在迭代器块中,yield 关键字与 return 关键字结合使用,向枚举器对象提供值. 这是一个返回值,例如,在 forea ...

  10. C# 基础小知识之yield 关键字 语法糖

    原文地址:http://www.cnblogs.com/santian/p/4389675.html 对于yield关键字我们首先看一下msdn的解释: 如果你在语句中使用 yield 关键字,则意味 ...

随机推荐

  1. csv数据集按比例分割训练集、验证集和测试集,即分层抽样的方法

    一.一种比较通俗理解的分割方法 1.先读取总的csv文件数据: import pandas as pd data = pd.read_csv('D:\BaiduNetdiskDownload\weib ...

  2. PWN 学习日志(1): pwntools简单使用与栈溢出实践

    常用的模块 模块 功能 asm 汇编与反汇编 dynelf 远程符号泄漏 elf 对elf文件进行操作 memleak 用于内存泄漏 shellcraft shellcode生成器 gdb 配合gdb ...

  3. boot-admin整合flowable官方editor-app进行BPMN2.0建模

    正所谓百家争鸣.见仁见智.众说纷纭.各有千秋!在工作流bpmn2.0可视化建模工具实现的细分领域,网上扑面而来的是 bpmn.js 这个渲染工具包和web建模器,而笔者却认为使用flowable官方开 ...

  4. 重新实现hashCode()方法

    在Java中,为了让对象在集合中能够更高效地进行查找和比较,我们通常需要重写对象的equals()和hashCode()方法.其中,equals()方法用于比较两个对象是否相等,而hashCode() ...

  5. javasec(五)URLDNS反序列化分析

    这篇文章介绍 URLDNS 就是ysoserial中⼀个利⽤链的名字,但准确来说,这个其实不能称作"利⽤链".因为其参数不是⼀个可以"利⽤"的命令,⽽仅为⼀个U ...

  6. ToF技术全解读

    文章目录 ToF技术全解读 1. 什么是ToF 2. ToF的原理 3. ToF 优缺点 ToF技术全解读 1. 什么是ToF ToF: Time of flight. 飞行时间.当然这只是一种翻译的 ...

  7. css知识点简记

    1.改变position: fixed; 定位基准元素的方式,父级以上元素的: ① tranform属性值不为none的元素 ② perspective值不为none的元素 ③ will-change ...

  8. 第十四届蓝桥杯省赛C++ B组(个人经历 + 题解)

    参赛感受 这是我第一次参加蓝桥杯的省赛,虽然没什么参赛经验,但是自己做了很多前几届蓝桥杯的题,不得不说,这一届蓝桥杯省赛的难度相较于之前而言还是比较大的.之前很流行蓝桥杯就是暴力杯的说法,但是随着参赛 ...

  9. python中的一些解码和编码

    开头 最近爬取百度贴吧搜索页的时候遇到一个url的编码问题,颇为头疼,记录下来防止下次忘记 工具网站 解码编码的工具网站推荐 http://tool.chinaz.com/tools/urlencod ...

  10. 2022-09-29:在第 1 天,有一个人发现了一个秘密。 给你一个整数 delay ,表示每个人会在发现秘密后的 delay 天之后, 每天 给一个新的人 分享 秘密。 同时给你一个整数 forg

    2022-09-29:在第 1 天,有一个人发现了一个秘密. 给你一个整数 delay ,表示每个人会在发现秘密后的 delay 天之后, 每天 给一个新的人 分享 秘密. 同时给你一个整数 forg ...