首页 > 其他分享 >解读 ---- yield 关键字

解读 ---- yield 关键字

时间:2023-07-19 20:58:01浏览次数:129  
标签:异步 return 迭代 int yield ---- 关键字 static

合集 - c#基础(6)   1.编码技巧 --- 如何实现字符串运算表达式的计算07-122.编码技巧 --- 同步锁对象的选定07-13 3.解读 --- yield 关键字07-17 4.并发编程 --- 信号量线程同步07-185.并发编程 ---为何要线程池化07-186.编码技巧 --- 谨防闭包陷阱07-19 收起  

引言

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 来创建异步生成器,在某些场景下可以实现更高效、可靠的异步编程。

 

 

出处:https://www.cnblogs.com/pandefu/p/17536297.html

标签:异步,return,迭代,int,yield,----,关键字,static
From: https://www.cnblogs.com/mq0036/p/17566697.html

相关文章

  • 并发编程 ---- 信号量线程同步
    合集-c#基础(6) 1.编码技巧---如何实现字符串运算表达式的计算07-122.编码技巧---同步锁对象的选定07-133.解读---yield关键字07-174.并发编程---信号量线程同步07-185.并发编程---为何要线程池化07-186.编码技巧---谨防闭包陷阱07-19收起 引言上......
  • Android 妙用TextView实现左边文字,右边图片
    原文:Android妙用TextView实现左边文字,右边图片-Stars-One的杂货小窝有时候,需要文字在左边,右边有个箭头,我个人之前会有两种做法:使用线性布局来实现或者使用约束布局,一个左对齐,一个右对齐这几天突然想到是否可以使用TextView的设置图标的方式实现,研究发现确实可......
  • 并发编程 ----为何要线程池化
    合集-c#基础(6) 1.编码技巧---如何实现字符串运算表达式的计算07-122.编码技巧---同步锁对象的选定07-133.解读---yield关键字07-174.并发编程---信号量线程同步07-185.并发编程---为何要线程池化07-186.编码技巧---谨防闭包陷阱07-19收起 引言众......
  • 函数式编程-part1概述和理解
    为什么学?能够看懂公司里的代码大数量下处理集合效率高,因为有并行流,而自己创建处理会有很多问题代码可读性高消灭嵌套地狱本系列将从Lambda表达式、Stream流、Optional、函数式接口、方法引用等顺序开始讲解//查询未成年作家的评分在70以上的书籍由于洋流影响所......
  • 属性
          ......
  • Eigen库操作
    #include<iostream>#include<eigen3/Eigen/Dense>usingnamespacestd;usingnamespaceEigen;intmain(){Matrix2fss;ss<<2.3f,3.2f,3.4f,3.1f;cout<<ss<<endl;cout<<"======="<<endl;......
  • 编码技巧 --- 谨防C#闭包陷阱
    合集-c#基础(6) 1.编码技巧---如何实现字符串运算表达式的计算07-122.编码技巧---同步锁对象的选定07-133.解读---yield关键字07-174.并发编程---信号量线程同步07-185.并发编程---为何要线程池化07-186.编码技巧---谨防闭包陷阱07-19收起 引言先......
  • P4136 谁能赢呢?
    题目描述小明和小红经常玩一个博弈游戏。给定一个n×n的棋盘,一个石头被放在棋盘的左上角。他们轮流移动石头。每一回合,选手只能把石头向上,下,左,右四个方向移动一格,并且要求移动到的格子之前不能被访问过。谁不能移动石头了就算输。假如小明先移动石头,而且两个选手都以最优策略......
  • springboot整合mybatis
    项目结构: 1.添加依赖<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://mave......
  • c语言学习7
    函数传参1、函数中定义的变量属于该函数,出了该函数就不能再被别的函数直接使用2、实参与形参之间是以赋值的方式进行传递数据的,并且是单向值传递3、return语句其实是把返回值数据放入公共区域内存中(调用者和被调用者都可以访问),调用者会从该区域获取返回值;如果不写return语句,......