在上一篇文章中,我们看了一下枚举器以及.NET如何使用foreach循环,我们看到了枚举器实际上是如何通过使用MoveNext方法和Current属性从一个状态转换到另一个状态的对象。

我们知道,如果我们想要创建一个自定义枚举器,我们将需要实现IEnumerator接口或它的泛型 副本,这是状态发挥作用和状态机的地方。查看枚举器的Current属性是如果成为对象还是泛型类型,我们可以利用这个优势,并实现从计算、到枚举,甚至整个工作流程的各种算法。所有“魔法”实际上都发生在MoveNext方法中,我们可以在其中执行任何从一个状态转换到另一个状态所需的操作。

所有这些对于理解我们是否希望通过实现IEnumerable接口或者泛型IEnumerable版本来实现我们自己的集合是必不可少的,因为我们必须告诉我们的集合应该如何遍历它,这意味着返回一个枚举器并实现MoveNext方法,基本上我们需要实现一个枚举器,或者如果我们在自己的实现中有一个内部集合,那么只需传递它。

但在大多数情况下,.NET提供的泛型集合绰绰有余,除非我们想要创建一个非常专业的迭代器或结构,如图形和二叉树。

这就把我们带到了关于.NET编译器在幕后做什么以使我们的生活更轻松的主题,我知道我们已经走了很长一段路,但是要更好地理解它是如何组合在一起的(所做的是值得的)。

现在输入我们的客人,yield关键字。为此,我准备了一个示例,以便更好地可视化yield使用的一个方面

public IEnumerable<int> Fibs (int fibCount)
{for (int i = 0, prevFib = 1, curFib = 1; i < fibCount; i++){yield return prevFib;int newFib = prevFib+curFib;prevFib = curFib;curFib = newFib;}
}

我们这里有一个返回所有“ fibCount” 斐波那契数列的方法,请注意yield关键字。当.NET编译器遇到yield关键字时,它会查看方法返回类型(在这种情况下,它是类型  int的IEnumerable),并在后台生成一个枚举器,因此这实际上会创建一个枚举整数的对象,因此  yield return组合是相当于枚举器的Current属性和方法的其余部分,直到满足另一个 yield ,这相当于  MoveNext方法。通过“另一个yield”,我的意思是,就像手动实现的枚举器一样,它将保留其当前状态并在下次遇到 yield时使用它 。所以在这种情况下,第一次时函数将返回1,第二次调用返回1,第三次调用返回2,依此类推,直到  yield超出范围或方法结束。

你可以在你的方法中拥有任意数量的yield语句,不需要将它放在循环中,并且它将始终从它执行的最后一个返回行继续,让我们看一个例子:

public IEnumerable<int> GetSomeIntegers()
{yield return 1;yield return 2;yield return 3;
}

此方法将返回1,然后在下一次调用时它将返回2,然后在下一次调用后它将返回3。

但是yield构造具有另一种形式,也就是yield break,它会告诉枚举器它已经到达其范围的末尾,以下是示范:

IEnumerable<string> Foo (bool breakEarly)
{yield return "One";yield return "Two";if (breakEarly)yield break;yield return "Three";
}

此示例仅返回“One”和“Two”,如果breakEarly参数为true,则永远不会达到“Three” 。

所以你看,使用yield return和yield break,我们可以设计一个复杂的工作流程,而无需实现我们自己的任何枚举器,并轻松使用外部参数。

接下来,我将向您展示一个违反正常执行流程的示例,并展示了LINQ如何通过其扩展方法在幕后工作,以及如何编写枚举器。

static void Main()
{foreach (int fib in EvenNumbersOnly(Fibs(6))){Console.WriteLine (fib);}
}static IEnumerable<int> Fibs (int fibCount)
{for (int i = 0, prevFib = 1, curFib = 1; i < fibCount; i++){yield return prevFib;int newFib = prevFib+curFib;prevFib = curFib;curFib = newFib;}
}static IEnumerable<int> EvenNumbersOnly (IEnumerable<int> sequence)
{foreach (int x in sequence)if ((x % 2) == 0)yield return x;
}

在这里,我们有一个枚举器组合的例子。乍一看,我们希望Fibs首先执行,但这是违反工作流逻辑的部分,程序将首先进入EvenNumberOnly方法,然后当它到达foreach内部时,它才会实际进入Fibs方法。然后它实际上将继续执行foreach直到它可以返回一个值,此时它会将它写入屏幕,然后该过程从它停止的地方再次开始,保持两个EvenNumberOnly和Fibs枚举器的状态直到Fibs完成,此时EvenNumberOnly也将完成。

这就是LINQ如何允许我们链接多个操作并“实时”处理大型数据集,而不是在每个步骤中遍历整个元素集合。使用这种技术,我们还可以处理来自Web服务的分页数据,而无需预先进行大量调用并将其存储在内存中。

尽管它是一个非常好用且有用的功能,但我们必须记住,该yield 构造有一些限制:

  • 该yield关键字只能用于返回IEnumerable形式的方法。
  • 该yield关键字不能被用在try- catch块(其理由是,当一个异常被抛出,则枚举变得无效并且它将被释放),但它可以被用在try- finally块。
  • 该方法不能包含ref或out参数。
  • 它不能用于unsafe块。
  • 它不能用在anonymous方法中,如lambda表达式。

总之,我们看到了如何实现自定义枚举器,而不必经历制作我们自己的自定义类型的麻烦,以及我们如何利用它foreach来做更多的事情而不仅仅是迭代一组项目。

原文地址:https://www.codeproject.com/Articles/1266944/IEnumerable-and-State-Machines

C# —— IEnumerable和状态机相关推荐

  1. IEnumerable 使用foreach 详解

    自己实现迭代器 yield的使用 怎样高性能的随机取IEnumerable中的值 我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么L ...

  2. 【转】先说IEnumerable,我们每天用的foreach你真的懂它吗?

    [转]先说IEnumerable,我们每天用的foreach你真的懂它吗? 我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么Linq ...

  3. .Net轻量状态机Stateless

    很多业务系统开发中,不可避免的会出现状态变化,通常采用的情形可能是使用工作流去完成,但是对于简单场景下,用工作流有点大财小用感觉,比如订单业务中,订单状态的变更,涉及到的状态量不是很多,即使通过简单的 ...

  4. IEnumerable和使用收益回报的递归

    本文翻译自:IEnumerable and Recursion using yield return I have an IEnumerable<T> method that I'm us ...

  5. C# IEnumerator/IEnumerable

    迭代器模式 与 C# IEnumerator/IEnumerable Jul 21, 2018 | 编程 |  阅读 迭代器模式 与 C# IEnumerator/IEnumerable Part1 ...

  6. QT学习之状态机框架

    状态机框架 创建状态机

  7. 如何保证Qt状态机的最佳性能

    如何保证Qt状态机的最佳性能 How to ensure the best Qt state machine performance 如果您使用Qt进行应用程序开发,并且使用状态机,那么很可能您正在使 ...

  8. 用测试驱动开发状态机

    用测试驱动开发状态机 Developing state machines with test-driven development 由于状态机模型在嵌入式系统中的广泛应用,本文探讨了在测试驱动开发(T ...

  9. SharePoint 工作流解决方案(一):顺序工作流和状态机工作流

    SharePoint 的工作流是基于 Workflow Foundation 的,我们就先谈谈 WF,只有对 WF 有正确的认识,才能找到 SharePoint 工作流的解决方案. Workflow ...

最新文章

  1. Jupyter notebook 的使用
  2. python多线程与GIL
  3. XML DOM Node List
  4. cognos java api_Cognos API Connection
  5. docker多个容器一起打包_如何链接多个Docker容器并封装结果?
  6. 球体动画Android,Android自定义View实现简单炫酷的球体进度球实例代码
  7. 无序数组求第k大的数 python_【python刷题】寻找数组中第K大/小的数
  8. ui界面表单设计素材模板,实用可临摹
  9. matlab的annotation,科学网—annotation in matlab Graph - 夏靖的博文
  10. java笔记_2020年java程序员、java工程师必备资料(内含java学习思维图、视频教程、源码笔记等)...
  11. 百度地图多点路线规划_自驾游路线规划神器:高德地图路书功能
  12. cad多个窗口并排显示_如何创建包含 CAD 导入和选择的仿真 App
  13. 十大免费SSL证书:网站免费添加HTTPS加密
  14. socket调试工具、socket调试软件、tcp调试工具、tcp调试软件(sokit)
  15. 一次微信公众号分享功能总结
  16. 微信活码裂变系统 利器
  17. Unity中实现高级相机操作——Cinemachine插件
  18. 《孙子兵法》帮你玩转团队管理
  19. 生成网站与发布网站的区别
  20. Java毕业设计_集美大学诚毅学院校友录系统设计与开发

热门文章

  1. ubuntu 20.04双系统安装_win10上跑Ubuntu不用虚拟机不用双系统!
  2. 对多用户分时系统最重要_互联网搜索引擎:让你的产品在最显眼的位置摆摊
  3. 春节海报背景素材,喜庆中国味!
  4. 插画素材模板 | 玩转电商促销季插画设计
  5. 优秀的电商精品素材就到优图
  6. 简约几何海报背景设计素材,可盐可甜
  7. UI设计干货素材|教你正确使用浮动按钮
  8. socket接收时信号量阻塞了会丢数据吗_浅谈Java网络编程——非阻塞I/O
  9. python爬火车票是不是违法_python利用selenium+requests+beautifulsoup爬取12306火车票信息...
  10. DPDK EAL parameters(DPDK环境抽象层参数)-原始版本(F-Stack配置文件的配置参数)