深入理解C#中yield return的用法

              <h1><a name="t0"></a><a name="t0"></a><a id="_0"></a>简介</h1>

yield return 是C#中特有的一个关键字,主要的使用是用于可遍历的数组的数据返回。具体如何理解,让我们通过一个实例来看一下。

实例

要求:写一个函数,返回指定范围的奇数。

这个需求很容易实现,常规的写法如下所示:

public IEnumerable<int> GetOddNumber(int start, int end)
{
    IEnumerable<int> list = new List<int>();
    for (int i = start; i <= end; i++)
        if (i % 2 == 1)
            list.Add(size);
    return list;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

通过提供这个函数,我可以做一些更多的操作,比如找出 [20, 30] 中的第1个素数。

foreach(int num in GetOddNumber(20, 30))
{
    if(IsPrim(num)) // IsPrim(int) 函数用于判断一个int是不是素数,代码略。
    {
    	Console.WriteLine(num);
    	break;
    }	
}         

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

运行后,结果显示为23,工作一切正常。

这里关于结果请注意,以上的需求完全可以用一个函数来实现,但是由于在实际应用中,第1个函数可能是编写后台类库的写的,而第2个函数,可能是前台的调用的,所以在实现时被分为两个函数。这种分隔非常常见,是类中提供的代码和客户端调用代码的一种常见方式。

问题

以上的示例中工作很好,但是这样的分工,往往可能会有问题。在以上的示例中,虽然需求只是找出第1个素数,但是在调用 GetOddNumber(20, 30) 时,返回的是所有满足条件的素数,即 [21, 23, 25, 27, 29] 5个数字,其中后面4个完全是多余的。在这个示例中,4个多余的数字倒也无所谓,可以忽略,但是在实际应用中,有很多情况数据量很大,是无法忽略的。这时候,我们往往要精巧地选择范围,要么使用特殊的方法另辟蹊径。

解决方案

我们解决问题,最好的方式就是以一种通用的方式来解决。这样可以适用大部分情况。为了解决以上的问题,我们就可以引入 yield return 来进行解决。我们使用这个关键字对 GetOddNumber(start, end) 进行重写,代码如下。

public IEnumerable<int> GetOddNumber(int start, int end)
{
    for (int i = start; i <= end; i++)
        if (i % 2 == 1)
            yield return i;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

客户端的代码,即上面的 foreach 循环,完全不用修改,并且运行的结果也是一样的。区别在于 GetOddNumber(start,end) 函数使用了 yield return 后,每次遍历时返回一个结果后,就处于休息状态,直到下次循环时才会继续。这样的好处有以下几点:

  • 减少循环次数
    yield return 是惰性的,每次循环到 yield return 时,直接返回给调用方满足条件的数据,不用去处理没循环到的数据,即在上例中,第1次循环GetOddNumber(start,end)返回 21给调用方,调用方判断21不满足条件,然后再次调用GetOddNumber(start,end),此时继续执行,返回23给调用方,此时调用方发现满足条件循环就结束了,而GetOddNumber(start,end)中的循环也只调用了2次,从而减少了循环次数。
  • 减少中间变量定义
    即不用再去定义一个 IEnumerable<int> list 来接收结果,有满足条件的数据直接返回即可,操作更加方便。

补充:如果想提前结束 GetOddNumber(start, end) 中的循环,可以在循环中使用 yield break 关键字。

扩展

有了以上的理解以后, 我们如果结合Lambda表达式,可以让代码更简单,比如同样是以上的示例,类的内部可以这么写

public IEnumerable<int> GetOddNumber(Fun<int, bool> fun, int start, int end)
{
    for (int i = start; i <= end; i++)
        fun(i)
            yield return i;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在调用方可以以更简洁的代码即可实现同样的功能,代码如下所示:

foreach(int num in GetOddNumber(n => IsPrim(n), 20, 30))
   Console.WriteLine(num);

 
 
  • 1
  • 2

结论

通过使用 yield return 我们可以更加方便高效地对数列进行操作,能够同时减少代码量和循环次数。所以在代码相应的地方推荐使用 yield return 代替使用中间数据的数列操作方法。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
yield returnC#的一个关键字,用于在迭代器方法返回一个元素,并暂停迭代器的执行,直到下一次迭代。通过使用yield return,可以在不立即计算所有元素的情况下,逐个返回元素。 引用\[1\]展示了一个使用yield return实现的过滤器方法。该方法通过遍历初始数据集合,并返回大于2的元素。每次调用迭代器方法时,只返回一个元素,并在下一次迭代时继续执行。 引用\[2\]展示了一个不使用yield return的实现。该方法通过创建一个结果列表,并在遍历初始数据集合时,将大于2的元素添加到列表。最后,返回结果列表。 引用\[3\]展示了一个正确使用yield return的示例。该示例使用yield return返回一个包含指定数量的Customer对象的迭代器。在使用foreach循环遍历迭代器时,只打印ID小于1000的Customer对象,并在遇到ID大于等于1000的对象时终止循环。 总结来说,yield returnC#用于在迭代器方法逐个返回元素的关键字。它可以帮助我们在不立即计算所有元素的情况下,按需返回元素,并暂停迭代器的执行。 #### 引用[.reference_title] - *1* *2* [C#yield return用法分析](https://blog.csdn.net/wojiuguowei/article/details/124604083)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [C# yield return详解。](https://blog.csdn.net/csdn2990/article/details/129664309)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值