记一次被yield return坑的历程。
事情的经过是这样的:
我用C#写了一个很简单的一个通过迭代生成序列的函数。
public static IEnumerable<T> Iterate<T>(this Func<T, T> f, T initVal, int length)
{
Checker.NullCheck(nameof(f), f);
Checker.RangeCheck(nameof(length), length, , int.MaxValue); var current = initVal;
while (--length >= )
{
yield return (current = f(current));
}
}
其中NullCheck用于检查参数是否为null,如果是则抛出ArgumentNullException异常。
对应的,我写了如下单元测试代码去检测这个异常。
public void TestIterate()
{
Func<int, int> f = null;
Assert.Throws<ArgumentNullException>(() => f.Iterate(, )); // Other tests
}
但是,这个测试出乎意料的fail了。
一开始,我以为是NullCheck函数的问题,可我把NullCheck直接换成了if语句,还是通不过。
后来我在Iterate函数下断点并调试。结果调试器根本没有停在断点上,直接运行完了测试。
我以为是我测试的方法不对,所以我不断的修改测试代码,甚至还一度以为是.NET的Unit Tests出了bug。
最终,我在这个测试代码发现了问题:
Assert.Throws<ArgumentNullException>(() =>
{
var seq = f.Iterate(, );
foreach (int ele in seq)
Console.WriteLine(ele);
});
当我调试这个测试时,程序停在了我之前在Iterate函数上下的断点。
于是,我在 var seq = f.Iterate(, ); 上下断点,并逐步运行。这时我发现,当程序运行到 var seq = f.Iterate(, ); 时并不会进入Iterate函数;而是当程序运行到foreach语句后才进入。
这就要涉及到yield return的具体工作流程。当函数代码中出现yield return,调用这个函数会直接返回一个IEnumerable<T>或IEnumerator<T>对象,并不会执行函数体的任何代码。这些代码都被封装到了返回对象的内部。它们会在你开始枚举的时候开始执行。
因此,上面两个Check并不会在函数调用时执行,而是在当你开始foreach的时候才执行。
这并不是我想要的结果。我希望在调用函数时就检查参数合法性,如果不合法便直接抛出异常。
解决这个问题有两种途径,一是把它拆成两个函数:
public static IEnumerable<T> Iterate<T>(this Func<T, T> f, T initVal, int length)
{
Checker.NullCheck(nameof(f), f);
Checker.RangeCheck(nameof(length), length, , int.MaxValue); return IterateWithoutCheck(f, initVal, length);
} private static IEnumerable<T> IterateWithoutCheck<T>(this Func<T, T> f, T initVal, int length)
{
var current = initVal;
while (--length >= )
{
yield return (current = f(current));
}
}
或者,你也可以将这个函数包装成一个类。
class FunctionIterator<T> : IEnumerable<T>
{
private readonly Func<T, T> f;
private readonly T initVal;
private readonly int length; public FunctionIterator(Func<T, T> f, T initVal, int length)
{
Checker.NullCheck(nameof(f), f);
Checker.RangeCheck(nameof(length), length, , int.MaxValue); this.f = f;
this.initVal = initVal;
this.length = length;
} public IEnumerator<T> GetEnumerator()
{
T current = initVal; for (int i = ; i < length; ++i)
yield return (current = f(current));
} System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
记一次被yield return坑的历程。的更多相关文章
- yield return
一次被yield return坑的历程. 事情的经过是这样的: 我用C#写了一个很简单的一个通过迭代生成序列的函数. public static IEnumerable<T> Iter ...
- yield学习续:yield return迭代块在Unity3D中的应用——协程
必读好文推荐: Unity协程(Coroutine)原理深入剖析 Unity协程(Coroutine)原理深入剖析再续 上面的文章说得太透彻,所以这里就记一下自己的学习笔记了. 首先要说明的是,协程并 ...
- 12.C#yield return和yield break及实际应用小例(六章6.2-6.4)
晚上好,各位.今天结合书中所讲和MSDN所查,聊下yield关键字,它是我们简化迭代器的关键. 如果你在语句中使用了yield关键字,则意味着它在其中出现的方法.运算符或get访问器是迭代器,通过使用 ...
- C#yield return和yield break
C#yield return和yield break 晚上好,各位.今天结合书中所讲和MSDN所查,聊下yield关键字,它是我们简化迭代器的关键. 如果你在语句中使用了yield关键字,则意味着它在 ...
- 可惜Java中没有yield return
项目中一个消息推送需求,推送的用户数几百万,用户清单很简单就是一个txt文件,是由hadoop计算出来的.格式大概如下: uid caller 123456 12345678901 789101 12 ...
- C#中的using和yield return混合使用
最近写代码为了为了省事儿用了几个yield return,因为我不想New一个List<T>或者T[]对象再往里放元素,就直接返回IEnumerable<T>了.我的代码里还有 ...
- yield return的用法简介
使用yield return 语句可一次返回一个元素. 迭代器的声明必须满足以下要求: 返回类型必须为 IEnumerable.IEnumerable<T>.IEnumerator 或 I ...
- yield return的作用
测试1: using UnityEngine; using System.Collections; public class test1 : MonoBehaviour { // Use this f ...
- C#中yield return用法分析
这篇文章主要介绍了C#中yield return用法,对比使用yield return与不使用yield return的流程,更直观的分析了yield return的用法,需要的朋友可以参考下. 本文 ...
随机推荐
- PHPsthdy+xdebug
PHPsthdy下载后查看phpinfo后会发现没有xdebug这一项: 1.phpStudy集成了XDebug扩展,所以不用单独下载XDebug. 2.打开XDebug扩展:右击PHPstudy的图 ...
- 前端数据存储方案集合(cookie localStorage等)以及详解 (一)
客户端.前端 存储 一. 起 因 首先解释下为什么想来写这个关于前端存储的问题,因为最近在做小程序相关的内容.但是,在开发过程中,我们难免会遇到 token 存储. 代码缓存. 图片存储等等. 以及可 ...
- nodeJS之URL
前面的话 在HTTP部分,详细介绍了URL的相关知识.而nodejs中的url模块提供了一些实用函数,用于URL处理与解析.本文将详细介绍nodeJS中的URL URL对象 解析 URL 对象有以下内 ...
- js验证15位或18位身份证
本篇文章是本人在网上搜集了一些验证,然后又个人进行一定修改的关于身份证的验证,欢迎修改指正..... function IdCardValidateRule(idCard) { var tip; ...
- 日本語の文法⇒ day1 限る型…意向形の紹介
1)限る型 ① 不仅仅 名+に限らず この漫画は子供に限らず.大人にも人気が高い. 最近は女性に限らず.男もダイエットに励む人が多い. ②仅仅....名+限って 当店は.本日に限って半額をセールを実施 ...
- 解决谷歌浏览器 chrome解决跨域(CORS)问题---chrome插件
chrome中跨域问题解决方案 插件解决, 插件地址 https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfb ...
- Section 1.1 Your Ride Is Here
Your Ride Is Here It is a well-known fact that behind every good comet is a UFO. TheseUFOs often com ...
- 70. Climbing Stairs【leetcode】递归,动态规划,java,算法
You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb ...
- 175. Combine Two Tables【LeetCode】-LEFT JON 和RIGHT JOIN,两张表关联查询-java -sql入门
Table: Person +-------------+---------+ | Column Name | Type | +-------------+---------+ | PersonId ...
- IAT重定向之修复
.. 仅允许非商业转载,转载请注明出处