通过实例解析c# yield关键字使用方法

1.yield实现的功能

yield return:

先看下面的代码,通过yield return实现了类似用foreach遍历数组的功能,说明yield return也是用来实现迭代器的功能的。

using static System.Console;
using System.Collections.Generic;

class Program
{
  //一个返回类型为IEnumerable<int>,其中包含三个yield return
  public static IEnumerable<int> enumerableFuc()
  {
    yield return 1;
    yield return 2;
    yield return 3;
  }

  static void Main(string[] args)
  {
    //通过foreach循环迭代此函数
    foreach(int item in enumerableFuc())
    {
      WriteLine(item);
    }
    ReadKey();
  }
}

输出结果:123

yield break:

再看下面的代码,只输出了1,2,没有输出3,说明这个迭代器被yield break停掉了,所以yield break是用来终止迭代的。

using static System.Console;
using System.Collections.Generic;
class Program
{
  //一个返回类型为IEnumerable<int>,其中包含三个yield return
  public static IEnumerable<int> enumerableFuc()
  {
    yield return 1;
    yield return 2;
    yield break;
    yield return 3;
  }

  static void Main(string[] args)
  {
    //通过foreach循环迭代此函数
    foreach(int item in enumerableFuc())
    {
      WriteLine(item);
    }
    ReadKey();
  }
}

输出结果:12

2.只能使用在返回类型必须为 IEnumerable、IEnumerable<T>、IEnumerator 或 IEnumerator<T>的方法、运算符、get访问器中。

3.yield关键字的实现原理

我们用while循环代替foreach循环,发现我们虽然没有实现GetEnumerator(),也没有实现对应的IEnumerator的MoveNext(),和Current属性,但是我们仍然能正常使用这些函数。

class Program
{
  //一个返回类型为IEnumerable<int>,其中包含三个yield return
  public static IEnumerable<int> enumerableFuc()
  {
    yield return 1;
    yield return 2;
    yield return 3;
  }

  static void Main(string[] args)
  {
    //用while循环代替foreach
    IEnumerator<int> enumerator = enumerableFuc().GetEnumerator();
    while (enumerator.MoveNext())
    {
      int current = enumerator.Current;
      WriteLine(current);
    }
    ReadKey();
  }
}

输出结果:123

至于为什么会出现这种情况,我们可以用ILSpy对生成的exe进行反编译来找到原因。

由于直接反编译成C#会变为原样

所以我们选择反编译为带C#注释的IL代码,虽然可读性差点,但是可以详细的了解其中过的原理。

先来看Program翻译的情况,编译的时候自动生成了一个新的类。

接下来我们来仔细看这些代码,EnumerableFuc()返回了这个新的类。

看这个代码自动生成的类的实现,发现它继承了IEnumerable、IEnumerable<T>、IEnumerator 或 IEnumerator<T>,这时我们应该已经能猜到这个新的类就是我们没有实现对应的IEnumerator的MoveNext(),和Current属性,但是我们仍然能正常使用这些函数的原因了。

我们再来看一下这个类具体是如何实现迭代的呢,我们主要来看一下MoveNext()函数

每次调用MoveNext()函数都会将state加1,一共进行了4次迭代,前三次返回true,最后一次返回false,代表迭代结束。这四次迭代对应被3个yield return语句分成4部分的enumberableFuc()中的语句。

用enumberableFuc()来进行迭代的真实流程就是:

1.运行enumberableFuc()函数,获取代码自动生成的类的实例。

2.接着调用GetEnumberator()函数,将获取的类自己作为迭代器开始迭代。

3.每次运行MoveNext(),state增加1,通过switch语句可以让每次调用MoveNext()的时候执行不同部分的代码。

4。MoveNext()返回false,结束。

这也能说明yield关键字其实是一种语法糖,最终还是通过实现IEnumberable<T>、IEnumberable、IEnumberator<T>和IEnumberator接口实现的迭代功能。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C#中的yield关键字的使用方法介绍

    yield不能单独放在try-catch块中,如果try中有yield那么,这个try块后面不许跟着finally块:也不能出现在匿名方法中,所以,看起来yield似乎并不常用,但是也不是不用.我前面有一个关于迭代器的例子<C#中的迭代器基础>中就用到了.可以参考一下那个例子,但是这里要再说的一点是我后来看到的,yield是跟return一起使用的,形式为yield return xxx,一般来说单独的return在每个方法中只能存在一个.而yield则不同的是,可以出现连续多个.迭代器,是一

  • C# yield关键字详解

    对于yield关键字我们首先看一下msdn的解释: 如果你在语句中使用 yield 关键字,则意味着它在其中出现的方法.运算符或 get 访问器是迭代器. 通过使用 yield 定义迭代器,可在实现自定义集合类型的 IEnumerable和 IEnumerator模式时无需其他显式类(保留枚举状态的类,有关示例,请参阅 IEnumerator<T>). yield是一个语法糖 看msdn 的解释总是让人感觉生硬难懂.其实yield关键字很好理解.首先我们对于性质有个了解.yield是一个语法糖

  • C#使用yield关键字让自定义集合实现foreach遍历的方法

    foreach遍历是C#常见的功能,而本文通过实例形式展现了C#使用yield关键字让自定义集合实现foreach遍历的方法.具体步骤如下: 一般来说当我们创建自定义集合的时候为了让其能支持foreach遍历,就只能让其实现IEnumerable接口(可能还要实现IEnumerator接口) 但是我们也可以通过使用yield关键字构建的迭代器方法来实现foreach的遍历,且自定义的集合不用实现IEnumerable接口 注意:虽然不用实现IEnumerable接口 ,但是迭代器的方法必须命名为

  • 解析c# yield关键字

    1.yield实现的功能 yield return: 先看下面的代码,通过yield return实现了类似用foreach遍历数组的功能,说明yield return也是用来实现迭代器的功能的. using static System.Console; using System.Collections.Generic; class Program { //一个返回类型为IEnumerable<int>,其中包含三个yield return public static IEnumerable&

  • 关于C#中yield关键字的深入解析

    前言 前段时间了解到yield关键字,一直觉得还不错.今天给大家分享一下yield关键字的用法.yield return 返回集合不是一次性返回所有集合元素,而是一次调用返回一个元素.具体如何使用yield return 返回集合呢?我们一起往下面看吧. yield使用介绍 yield return 和yield break: 我们看下平常循环返回集合的使用操作(返回1-100中的偶数): class Program { static private List<int> _numArray;

  • C#使用yield关键字构建迭代器详解

    以前,如果我们希望构建支持foreach枚举的自定义集合,只能实现IEnumerable接口(可能还有IEnumerator()),返回值还必须是IEnumerator类型,除此之外还可以通过迭代器来使用构建foreach循环的类型,详细见下链接. 代码 public class Car { //内部状态数据 public int CurentSpeed; public int MaxSpeed; public string name; //汽车能不能用 private bool carIsde

  • 通过实例解析c# yield关键字使用方法

    1.yield实现的功能 yield return: 先看下面的代码,通过yield return实现了类似用foreach遍历数组的功能,说明yield return也是用来实现迭代器的功能的. using static System.Console; using System.Collections.Generic; class Program { //一个返回类型为IEnumerable<int>,其中包含三个yield return public static IEnumerable&

  • 通过实例解析Java List正确使用方法

    List是Java中比较常用的集合类,关于List接口有很多实现类,本文就来简单介绍下其中几个重点的实现ArrayList.LinkedList和Vector之间的关系和区别. List List 是一个接口,它继承于Collection的接口.它代表着有序的队列.当我们讨论List的时候,一般都和Set作比较. List中元素可以重复,并且是有序的(这里的有序指的是按照放入的顺序进行存储.如按照顺序把1,2,3存入List,那么,从List中遍历出来的顺序也是1,2,3). Set中的元素不可

  • Java多线程之volatile关键字及内存屏障实例解析

    前面一篇文章在介绍Java内存模型的三大特性(原子性.可见性.有序性)时,在可见性和有序性中都提到了volatile关键字,那这篇文章就来介绍volatile关键字的内存语义以及实现其特性的内存屏障. volatile是JVM提供的一种最轻量级的同步机制,因为Java内存模型为volatile定义特殊的访问规则,使其可以实现Java内存模型中的两大特性:可见性和有序性.正因为volatile关键字具有这两大特性,所以我们可以使用volatile关键字解决多线程中的某些同步问题. volatile

  • Java多线程状态及方法实例解析

    这篇文章主要介绍了Java多线程状态及方法实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 首先介绍线程的五种状态: 新生态:New Thread() 就绪态:准备抢CPU时间片 运行态:抢到了CPU时间片 阻塞态:放弃已经抢到的CPU时间片,且暂时不参与争抢 死亡态:Run运行完了之后 接下来介绍三种方法:线程的阻塞,线程的优先级设置,线程的礼让 public class MutliThreadDemo4 { public static

  • xtemplate node.js 的使用方法实例解析

    工程下安装XTemplate并使用它的方法实例说明: 1.安装xtpl 复制代码 代码如下: npm install xtpl xtemplate --save 2.在views目录添加test.xtpl文件,其内容为 this is {{title}}! 4.集成到Express中,只需要在app.js中,设置模板引擎即可 var print = require('./routes/print'); //此行代码放入app.js的require 声明代码段下边 app.set('view en

  • jQuery解析XML与传统JavaScript方法的差别实例分析

    本文实例讲述了jQuery解析XML与传统JavaScript方法的差别.分享给大家供大家参考.具体分析如下: 1. 传统javascript方法: var xmlDoc = request.responseXML; try // Build Markers, if available { var markers = xmlDoc.getElementsByTagName("marker") ; for ( var i = 0; i < markers.length ; i++

  • 实例解析Json反序列化之ObjectMapper(自定义实现反序列化方法)

    对于服务器端开发人员而言,调用第三方接口获取数据,将其"代理"转化并返给客户端几乎是家常便饭的事儿.    一般情况下,第三方接口返回的数据类型是json格式,而服务器开发人员则需将json格式的数据转换成对象,继而对其进行处理并封装,以返回给客户端. 在不是特别考虑效率的情况下(对于搜索.缓存等情形可以考虑使用thrift和protobuffer),通常我们会选取jackson包中的ObjectMapper类对json串反序列化以得到相应对象.通常会选取readValue(Strin

随机推荐