百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

全面掌握c# 的 yield return:使用 yield return 一次返回一个项目

bigegpt 2024-09-03 10:57 3 浏览

今天,我想向您展示两种从 C# 方法中操作和返回列表的方法。

首先,我将通过创建一个空列表并用所需信息填充然,后返回它。演示执行此操作的“常规”方法。

第二种方法是使用 yield return,这是一种使用枚举的简洁方法,通过一次返回一个项目来获得更简洁的方法。

常规方法

返回值列表的常规方法是先初始化一个空列表,然后遍历所需的值并将它们添加到列表中。

一旦你完成了必要的操作并将所有你想要的值添加到列表后,你就可以返回它。

使用此方法,您将始终先构建整个列表,然后将其返回给调用它的人。

假设您有一个方法,获得一个数字列表并返回这些数字的双倍值。 这就是您按照常规方式执行此操作的方式:

public List<int> GetDoubledNumbers(List<int> list)
{
    var result = new List<int>();
    foreach(var i in list)
    {
        result.Add(i * 2);
    }
    return result;
}

现在,打印这些列表。 这只是一个普通函数,它遍历数字列表并打印出来。

void PrintNumbers(IEnumerable<int> list)
{
    Console.WriteLine();
    
    foreach(var i in list)
    {
        Console.Write(i + ", ");
    }
}

现在,如果你想停止操作列表,你只需根据条件在循环中添加一个 break 语句。 就像下面的例子:

public List<int> GetDoubledNumbersIfMoreThanThreshhold(List<int> list, int threshhold)
{
    var result = new List<int>();
    foreach(var i in list)
    {
        if (i >= threshhold)
        {
            break;
        }
        
        result.Add(i * 2);
    }
    
    return result;
}

Yield 上下文关键字

返回列表的另一种方法是通过关键字 yield return。 在这里,它不会返回整个列表,而是始终返回一个枚举(值),并且只在迭代遍历列表成员后才执行逻辑。 所以,基本上,它不是返回完整的项目列表,而是一次返回一个项目。

这种方法的优点是,您只在迭代中轮到它时才处理迭代中的逻辑(例如,在 foreach 中)。

public IEnumerable<int> GetDoubledNumbers(List<int> list)
{
    foreach (var i in list)
    {
        Console.Write("*"); // 用于证明此方法仅在调用此方法后的迭代(循环)中轮到它执行的代码
        
        yield return i * 2;
    }
}

现在像这样运行它:

var numbers = new List<int>
{
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10
};

var yieldList = GetDoubledNumbers(numbers);

PrintNumbers(yieldList);

// 输出将是
// *2, *4, *6, *8, *10, *12, *14, *16, *18, *20,
//而不是
// 2, 4, 6, 8, 10, 12, 14, 16, 18, 20,

IEnumerable<int> GetDoubledNumbers(List<int> list)
{
    foreach (var i in list)
    {
        Console.Write("*"); // 用于证明此方法仅在调用此方法后的迭代(循环)中轮到它执行的代码
        
        yield return i * 2;
    }
}

void PrintNumbers(IEnumerable<int> list)
{
    Console.WriteLine();
    
    foreach(var i in list)
    {
        Console.Write(i + ", ");
    }
}

请注意,PrintNumbers 的结果不是

2, 4, 6, 8, 10, 12, 14, 16, 18, 20,

而是

*2, *4, *6, *8, *10, *12, *14, *16, *18, *20,

* 在 GetDoubledNumbers 迭代运行时插入,而不是在您调用它时插入。 这是因为当我们调用 GetDoubleNumbers 时,代码并没有立即执行。 一旦 foreach 循环开始一次返回一个项目,它只会在每次迭代中执行。

因此,只有在您遍历它时才会抛出异常。 所以调试可能会有点混乱。

要记住的另一件事是,您不能将 yield return 语句包装在 try-catch 块中。 如果你想有异常处理,你必须在调用 yield return 之前,将你想要的逻辑放在 try-catch 块中。 像下面的代码:

var numbers = new List<int>
{
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10
};

var yieldList = GetDoubledNumbers(numbers);

PrintNumbers(yieldList);


IEnumerable<int> GetDoubledNumbers(List<int> list)
{
    foreach (var i in list)
    {
        try
        {
     
          Console.Write("*"); 
        }
        catch (Exception e)
        {
          // 在这里做异常处理
          throw;
        }
        
        yield return i * 2;
    }
}

void PrintNumbers(IEnumerable<int> list)
{
    Console.WriteLine();
    
    foreach(var i in list)
    {
        Console.Write(i + ", ");
    }
}

Yield break

现在,如果您有一个循环但想根据您定义的条件停止它,您通常会在正常循环中使用 break 关键字。

当然,您可以在使用 yield return 时使用它,但这会打破循环,运行它下面的代码。

var numbers = new List<int>
{
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10
};

var result = GetDoubledNumbersIfMoreThanThreshhold(numbers, 5);

PrintNumbers(result);
// 输出将会是
// 2, 4, 6, 8, *Break* *End Method*

IEnumerable<int> GetDoubledNumbersIfMoreThanThreshhold(List<int> list, int threshhold)
{
    foreach (var i in list)
    {
        if (i >= threshhold)
        {
            Console.Write("*Break*");
            break;
        }
        yield return i * 2;
    }

    Console.Write(" *End Method*");
}

void PrintNumbers(IEnumerable<int> list)
{
    Console.WriteLine();
    
    foreach(var i in list)
    {
        Console.Write(i + ", ");
    }
}

请注意,*End Method* 也被输出了。 这是因为,仅使用 break 关键字,您就可以停止 foreach 循环,而不是立即停止整个枚举。

如果你只想停止枚举的执行,你必须使用 yield break 你可以从你的方法内部中断整个迭代。

var numbers = new List<int>
{
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10
};

var result = GetDoubledNumbersIfMoreThanThreshhold(numbers, 5);

PrintNumbers(result);
// 输出将会是
// 2, 4, 6, 8, *Break*

IEnumerable<int> GetDoubledNumbersIfMoreThanThreshhold(List<int> list, int threshhold)
{
    foreach (var i in list)
    {
        if (i >= threshhold)
        {
            Console.Write("*Break*");
            yield break;
        }
        yield return i * 2;
    }

    Console.Write(" *End Method*");
}

void PrintNumbers(IEnumerable<int> list)
{
    Console.WriteLine();
    
    foreach(var i in list)
    {
        Console.Write(i + ", ");
    }
}

Async

最后,如果您想在枚举方法中处理异步代码,则不能只返回 Task<IEnumerable<T>>。 如果您尝试这样做,您将收到以下警告:

The body of 'GetDoubledNumbersIfMoreThanThreshhold(List<int>, int)' cannot be an iterator block because 'Task<IEnumerable<int>>' is not an iterator interface type

那是因为现在您正在创建和使用异步枚举流,这不适用于返回task。

为了能够正常工作,您应该返回一个 IAsyncEnumerable<T>,它是异步流的返回类型。 这基本上意味着每次迭代都会在请求时被调用。

为了遍历,不会使用标准的 foreach 循环,而是在它之前添加一个 await,使循环被称为 await foreach。

因此,要让异步代码正常运行,您可以像下面的代码那样做:

var numbers = new List<int>
{
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10
};

var result = GetDoubledNumbersAsync(numbers);

await PrintNumbersAsync(result);

async IAsyncEnumerable<int> GetDoubledNumbersAsync(List<int> list)
{
    foreach (var i in list)
    {
        await Task.Delay(1000);
        
        yield return i * 2;
    }
}

async Task PrintNumbersAsync(IAsyncEnumerable<int> list)
{
    Console.WriteLine();
    
    await foreach(var i in list)
    {
        Console.Write(i + ", ");
    }
}

这段代码的运行非常有趣:

一旦迭代时间到了,您可以看到数字是如何打印的。

总结

我们已经了解了通过 C# 方法构建和返回项目列表的两种方法之间的区别。

我们探索了 yield return 的强大功能以及它如何保持代码整洁。 但是您也看到了应该如何谨慎使用它,因为您的方法调用它不会返回整个项目列表,而是一旦开始迭代,结果枚举就会一次返回一个项目。

最后,如果您想通过返回 IAsyncEnumerable 并使用 await foreach 循环来使用 yield return,您可以学习如何使用异步代码。

相关推荐

如何使用Java API操作HDFS系统?(hdfs java api的常见环境准备?)

1.搭建项目环境打开Eclipse选择FileàNewàMavenProject创建Maven工程,选择“Createasimpleproject”选项,点击【Next】按钮,会进入“New...

DataX写插件开发-集成阿里云RocketMQ

在上一期我们对datax进行了技术调研DataX数据异构、数据同步神器,这一次我们集成一个RocketMQ写插件,能够非常方便对将mysql数据同步到MQ中,下面来总结下具体步骤。1.下载datax源...

以SpringMVC+Shiro+Mybatis为核心开发的精简后台系统源码分享

项目说明源码获取方式:关注转发之后私信回复【源码】即可免费获取到以SpringMVC+Shiro+Mybatis为核心开发的精简后台基础系统。包含用户管理,角色管理,部门管理,权限管理,菜单管理,日志...

手把手教小伙伴们使用 Nginx 部署 TienChin 项目!

今天我就来手把手教小伙伴们部署TienChin项目,一起把这个项目跑起来,看看到底是个什么样的项目。小伙伴们知道,对于这种前后端分离的项目,我们在实际部署的时候,可以按照前后端分离的方式来部署,也...

推荐一款超棒的SpringCloud 脚手架项目

之前接个私活,在网上找了好久没有找到合适的框架,不是版本低没人维护了,在不就是组件相互依赖较高。所以我自己搭建一个全新spingCloud框架,里面所有组件可插拔的,集成多个组件供大家选择,喜欢哪个用...

SpringCloud 微服务迁移到 Kubernetes 容器化完整流程

k8s容器部署流程具体步骤:第一步:熟悉SpringCloud微服务项目第二步:源代码编译构建第三步:构建项目镜像并推送到镜像仓库第四步:K8s服务编排第五步:部署服务所需的基础环境第六步:部署微服...

SpringBoot 实现动态配置及项目打包部署上线

一、动态配置文件我们需要了解Spring动态指定配置文件的方式,来提高我们的部署效率。1.1、概述在实际企业开发中,开发环境、测试环境、生产环境通常采用不同的数据库等中间件的连接方式。如果此时我们按照...

3.5 源码安装ONOS1.3.0(源码包怎么安装)

ONOS是由ON.Lab使用Java及Apache实现发布的首款开源的SDN网络操作系统,主要面向服务提供商和企业骨干网。近日笔者在学习ONOS的过程中写下了这篇文章,希望可以对刚接触ONOS的同学们...

jenkins+gitlab 实现自动化部署(jenkins配置git自动部署)

目录1、安装jdk,要记住安装路径2、安装maven,要记住安装路径3、安装git,要记住安装路径4、安装gitlab5、安装jenkins(centos7)创建安装目录下载通用war包启动和关闭Je...

CI&amp;CD落地实践6-Jenkins接入maven构建后端springboot项目

前言在前面一篇《CI&CD落地实践5-Jenkins分布式环境搭建及多节点运行》中,我们介绍了如何在Windows及Linux系统上部署Jenkins从节点,本章节介绍如何在Jenkins创建mave...

从0到1体验Jenkins+Docker+Git+Registry实现CI自动化发布

阅读目录:一、前言二、发布流程三、环境准备四、部署思路梳理五、三台机器上操作六、Git机器上操作七、Docker机器上操作八、Jenkins机器上操作九、上传JAVA项目代码到Git仓库十、Jenki...

微服务架构实战:使用Jenkins实现自动化构建

使用Jenkins实现自动化构建一个大型平台的微服务架构设计通常会产生很多项目工程,因此会有很多服务和应用需要部署,并且需要不断地迭代和更新,这是一个庞大的工程,所以我们需要借助自动化工具,实现各个微...

Jenkins 自动化部署实例讲解(jenkins自动化部署git 项目)

前言你平常在做自己的项目时,是否有过部署项目太麻烦的想法?如果你是单体项目,可能没什么感触,但如果你是微服务项目,相信你应该是有过这种感触的。这种情况下,我一般会劝你了解一下Jenkins这个玩意...

多模块的微服务项目容器化与Git追踪发布记录

在使用了微服务后,一个项目往往由多个模块组成,而容器化发布的建议是单个容器尽量只运行单个进程。所以我们会把每个模块单独打包成镜像运行。如果每个模块都单独配置Dockerfile会让我们维护起来很麻烦。...

手把手教你使用 Jenkins+Docker 实现持续集成

作者:乐之终曲来源:https://blog.csdn.net/qq_37143673/对于Jenkins我只能用两个字形容,难用。就不过多吐槽了,本篇是基于docker环境的使用。1.安...