打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
初次使用C#中的yield

初次使用C#中的yield

  这几天在Python程序员的微信订阅号中总是见到yield的关键字,才想起来在C#中也是有yield,但是只是知道有,从来没有了解过他的用法,今天有时间就来看看是怎么使用的。刚开始肯定就是搜索一下用法了,找到两篇说明示例,一是 C# 中的"yield"使用,第二个是MSDN的官方api yield(C# 参考)

说实话第一个示例看完还是很模糊的概念,例子也没有看懂是在干嘛,一直到MSDN中给出结果集我才明白了到底的用法是怎么样的。

先来举例一个需求: 一个方法返回一个IEnumerable 类型结果集(例如返回一个list<int>的结果),通常的代码是这样的

 

 1         /// <summary> 2         ///  3         /// </summary> 4         /// <returns></returns> 5         public IEnumerable<int> Method() 6         { 7             List<int> results = new List<int>(); 8             int counter = 0; 9             int result = 1;10 11             while (counter++ < 10)12             {13                 result = result * 2;14                 results.Add(result);15             }16             return results;17         }

这样就完成了需求。

但是 有了yield之后就可以这样写了

 1         /// <summary> 2         ///  3         /// </summary> 4         /// <returns></returns> 5         public IEnumerable<int> YieldDemo() 6         { 7             int counter = 0; 8             int result = 1; 9             while (counter++ < 10)10             {11                 result = result * 2;              12                 yield return result;13             }14         }

两种效果是一样的,但是从我个人而言我喜欢第二个,感觉更简洁一些。

题外话:在写这两个例子中我又增加了一个知识点

  返回值IEnumerable其实和IEnumerable<object>是等价的,只是IEnumerable的结果需要动态的解析; 

 

但是还搞不清楚这两者实现有什么区别,所以我想看看这两个在做同一件事的时候效率如何,下面来尝试使用while循环10000000的取数据的耗时比较

使用 BenchmarkDotNet  测试结果如下

使用Stopwatch测试结果也是一样的

从这个测试里面可以看出YieldDemo方法几乎没有耗时,但是实际情况是不可能的吧,所以我又尝试做了遍历的测试

 

 1             Stopwatch stop = new Stopwatch(); 2             stop.Start(); 3             var res = new YieldTest().YieldDemo(); 4             foreach (var item in res) 5             { 6  7             } 8             var a = stop.ElapsedMilliseconds; 9             stop.Restart();10 11 12             var rrrrr = new YieldTest().Method();13             foreach (var item in rrrrr)14             {15 16             }17             var b = stop.ElapsedMilliseconds;18             stop.Restart();

这个测试的结果是a=168,b=142.对比上一个测试结果让我更加疑惑,我就开始打断点,看看执行的顺序是怎样的。

结果如下:  

     在 第三行 断点压根就没有进YieldDemo这个方法,而是当进行foreach 遍历结果的时候,才开始进入了YieldDemo这个方法,更奇怪的是每次的foreach 都会进入YieldDemo的while一次去取数据

这个结果让我有点懵了,只能再仔细看看文档解析,

  迭代器方法运行到 yield return 语句时,会返回一个 expression,并保留当前在代码中的位置。 下次调用迭代器函数时,将从该位置重新开始执行。 可以使用 yield break 语句来终止迭代。 

貌似这里面是涉及到了迭代器的东西。马上找迭代器的知识点,在 详解C# 迭代器 中看到这样一句解释

  需要强调的一点是,对于迭代块,虽然我们写的方法看起来像是在顺序执行,实际上我们是让编译器来为我们创建了一个状态机。这就是在C#1中我们书写的那部分代码---调用者每次调用只需要返回一个值,因此我们需要记住最后一次返回值时,在集合中位置。

  当编译器遇到迭代块时,它创建了一个实现了状态机的内部类。这个类记住了我们迭代器的准确当前位置以及本地变量,包括参数

这句话貌似解析了上面的疑问,但是看的有点云里雾里,还要花时间消化一下里面的具体原理 。

官方提示使用yield有一些限制,需要注意

1 不能将 yield return 语句置于 try-catch 块中。 可将 yield return 语句置于 try-finally 语句的 try 块中。2 可将 yield break 语句置于 try 块或 catch 块中,但不能将其置于 finally 块中。3 如果 foreach 主体(在迭代器方法之外)引发异常,则将执行迭代器方法中的 finally 块。4 匿名方法。 有关详细信息,请参阅匿名方法。5 包含不安全的块的方法。 有关详细信息,请参阅unsafe。

 

针对第一点让我感觉使用好有限制,为啥不能在try-catch 中使用呢?

关于其他的一些使用方法在MSDN里面都有详细的讲解,感觉没有什么好多说的。


转载:https://www.cnblogs.com/xiao99/p/6098285.html

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
什么是迭代器,创建并使用迭代器.为整数列表创建迭代器.为泛型列表创建迭代器 - さびしい....
C#迭代器
【转】【C#】迭代器IEnumerable和IEnumerator
C#迭代
C#中的foreach和yield
【译】使用C# yield关键字来提高性能和可读性
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服