• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C#教程 > C#特性 迭代器(下) yield以及流的延迟计算

C#特性 迭代器(下) yield以及流的延迟计算

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了yield迭代器,c#yield,c#yield return,c#yield是什么意思,yield等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

从0遍历到20(不包括20),输出遍历到的每个元素,并将大于2的所有数字放到一个IEnumerable<int>中返回

解答1:(我以前经常这样做)

static IEnumerable<int> WithNoYield()
    {
      IList<int> list = new List<int>();
      for (int i = 0; i < 20; i++)
      {
        Console.WriteLine(i.ToString());
        if(i > 2)
          list.Add(i);
      }
      return list;
    }
</div>

解答2:(自从有了C# 2.0我们还可以这样做)

static IEnumerable<int> WithYield()
    {
      for (int i = 0; i < 20; i++)
      {
        Console.WriteLine(i.ToString());
        if(i > 2)
          yield return i;
      }
    }
</div>

如果我用下面这样的代码测试,会得到怎样的输出?
测试1:

测试WithNoYield()

static void Main()
        {
            WithNoYield();
            Console.ReadLine();
        }
</div>

测试WithYield()

static void Main()
        {
            WithYield();
            Console.ReadLine();
        }
</div>

测试2:
测试WithNoYield()

static void Main()
        {
            foreach (int i in WithNoYield())
            {
                Console.WriteLine(i.ToString());
            }
            Console.ReadLine();
        }
</div>

测试WithYield()

static void Main()
        {
            foreach (int i in WithYield())
            {
                Console.WriteLine(i.ToString());
            }
            Console.ReadLine();
        }
</div>

给你5分钟时间给出答案,不要上机运行

*********************************5分钟后***************************************

测试1的运算结果
测试WithNoYield():输出从0-19的数字
测试WithYield():什么都不输出
测试2的运算结果
测试WithNoYield():输出1-19接着输出3-19
测试WithYield():输出12334455…….
(为节省空间上面的答案没有原样粘贴,可以自己运行测试)

 

是不是感到很奇怪,为什么使用了yield的程序表现的如此怪异呢?

测试1中对WithYield()的测试,明明方法调用了,居然一行输出都没有,难道for循环根本没有执行?通过断点调试果然如此,for循环根本没有进去,这是咋回事?测试2中对WithYield()的测试输出是有了,不过输出怎么这么有趣?穿插着输出,在foreach遍历WithYield()的结果的时候,好像不等到最后一条遍历完,WithYield()不退出,这又是怎么回事?

还是打开IL代码瞧一瞧到底发生了什么吧

Main方法的IL代码:

.method private hidebysig static void Main() cil managed
{
  .entrypoint
  .maxstack 1
  .locals init (
    [0] int32 i,
    [1] class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> CS$5$0000)
  L_0000: call class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> TestLambda.Program::WithYield()
  L_0005: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
  L_000a: stloc.1 
  L_000b: br.s L_0020
  L_000d: ldloc.1 
  L_000e: callvirt instance !0 [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
  L_0013: stloc.0 
  L_0014: ldloca.s i
  L_0016: call instance string [mscorlib]System.Int32::ToString()
  L_001b: call void [mscorlib]System.Console::WriteLine(string)
  L_0020: ldloc.1 
  L_0021: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
  L_0026: brtrue.s L_000d
  L_0028: leave.s L_0034
  L_002a: ldloc.1 
  L_002b: brfalse.s L_0033
  L_002d: ldloc.1 
  L_002e: callvirt instance void [mscorlib]System.IDisposable::Dispose()
  L_0033: endfinally 
  L_0034: call string [mscorlib]System.Console::ReadLine()
  L_0039: pop 
  L_003a: ret 
  .try L_000b to L_002a finally handler L_002a to L_0034
}
</div>

这里没什么稀奇的,在上一篇我已经分析过了,foreach内部就是转换成调用迭代器的MoveNext()方法进行while循环。我浏览到WithYield()方法:

private static IEnumerable<int> WithYield()
{
    return new <WithYield>d__0(-2);
}
</div>

晕,怎么搞的,这是我写的代码么?我的for循环呢?经过我再三确认,确实是我写的代码生成的。我心里暗暗叫骂,编译器,你怎么能这样“无耻”,在背后修改我的代码,你这不侵权么。还给我新生成了一个类<WithYield>d__0,这个类实现了这么几个接口:IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable(好啊,这个类将枚举接口和迭代器接口都实现了)
现在能解答测试1为什么没有输出了,调用WithYield()里面就是调用了一下<WithYield>d__0的构造方法,<WithYield>d__0的构造方法的代码:

public <WithYield>d__0(int <>1__state)
    {
        this.<>1__state = <>1__state;
        this.<>l__initialThreadId = Thread.CurrentThread.ManagedThreadId;
    }
</div>

这里没有任何输出。
在测试2中,首先我们会调用<WithYield>d__0的GetEnumerator()方法,这个方法里将一个整型局部变量<>1__state初始化为0,再看看MoveNext()方法的代码:

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C#特性 迭代器(下) yield以及流的延迟计算

相关文章

  • 2017-05-28C#实现判断当前操作用户管理角色的方法
  • 2017-05-28C#限速下载网络文件的方法实例
  • 2017-05-28ListView Adapter优化 实例
  • 2017-05-28C# WinForm 判断程序是否已经在运行,且只允许运行一个实例,附源码
  • 2017-05-28WinForm实现关闭按钮不可用或隐藏的方法
  • 2017-05-28C# 基础之运算符
  • 2017-05-28C#计算程序执行过程花费时间的方法
  • 2017-05-28C#中怎么将一个List转换为只读的
  • 2017-05-28c#读写App.config,ConfigurationManager.AppSettings 不生效的解决方法
  • 2017-05-28C#如何实现图片的剪裁并保存

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • 用 C# 编写一个停放在任务栏上的图标程序
    • c#几种数据库的大数据批量插入(SqlServer、Oracle、SQLite和MySql)
    • C#递归遍历窗体所有textbox控件并设置textbox事件的方法
    • C#拼图游戏编写代码(2)
    • C#实现子窗体与父窗体通信方法实例总结
    • C#获取文件创建时间的方法
    • 仿orm自动生成分页SQL分享
    • .NET/C#实现识别用户访问设备的方法
    • c#和avascript加解密之间的互转代码分享
    • C#中使用Interlocked进行原子操作的技巧

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有