C# yield关键字解析
相信好多程序员都是因为unity的协程(Coroutine)认识yield这个关键字的,知道在unity的开发中诸如yield return null、yield return new WaitForSeconds(1.0f)的用法,其实yield是C#的关键字,unity的协程只是在c#的基础上做了一层封装,我们现在来看看yield这个关键字。说到yield就不得不说迭代器,迭代器模式是设计模式的一种,因为其运用的普遍性,很多语言都有内嵌的原生支持。在.NET中,迭代器模式是通过IEnumerator、IEnumerable两个接口和两个同名的泛型接口来封装的:public interface IEnumerator { object Current { get; } bool MoveNext(); void Reset(); }IEnumerator只定义了一个属性、两个函数,Current为迭代器的当前值,通过调用MoveNext函数让迭代器的前进一步,返回值表示该迭代器是否结束,Reset函数用于重置数据。 public interface IEnumerable { IEnumerator GetEnumerator(); }IEnumerable更简单,返回迭代器。一般这两个接口的实现位于不同的类中。
foreach关键字之所以能方便对数组、List、Dictionary进行循环,其实也是在背后调用IEnumarator的MoveNext函数从头遍历到尾,取出每次的Current值,说白了它是个语法糖,在编译后会对我们的代码自动替换。我们来看下List的迭代器实现: public struct Enumerator : IEnumerator, System.Collections.IEnumerator { private List list; private int index; private int version; private T current; internal Enumerator(List list) { this.list = list; index = 0; version = list._version; current = default(T); } public void Dispose() { } public bool MoveNext() { List localList = list; if (version == localList._version && ((uint)index < (uint)localList._size)) { current = localList._items[index]; index++; return true; } return MoveNextRare(); } private bool MoveNextRare() { if (version != list._version) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); } index = list._size + 1; current = default(T); return false; } public T Current { get { return current; } } Object System.Collections.IEnumerator.Current { get { if( index == 0 || index == list._size + 1) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen); } return Current; } } void System.Collections.IEnumerator.Reset() { if (version != list._version) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); } index = 0; current = default(T); } }public class List : IEnumerable, ICollection, IList, ICollection, IEnumerable, IList{ IEnumerator IEnumerable.GetEnumerator() { return new Enumerator(this); }可以看到其实现是规规矩矩的继承了IEnumerator、IEnumerable及其两个泛型接口,一切都很完美,只有一个问题,是什么问题呢?答:写的太累了(手动滑稽)。终于引出了yield,没错,yield可以大大的简化迭代器代码,让Coder写起来更加轻松自在,我们的迭代代码可以这样写: public class Iteration: IEnumerable { public List lstInfo = new List() { 1, 3, 5, 7, 9, 11 }; public IEnumerator GetEnumerator() { for (int i = 0; i < lstInfo.Count; ++i) { yield return lstInfo[i]; } } }对于使用者来说方式还是一样: static void IterationTest() { Iteration obj = new Iteration(); foreach (var item in obj) { Console.WriteLine(item); } }当然啦,List的迭代器代码还是好多是关于版本号判断的,我们的示例并没有相关的逻辑,不过就算是加上,代码依然可以精简很多,这就是yield的魅力所在。有些人看到这可能还是迷惑,因为大部分的程序员的思路都是线性的,上面的Iteration类的GetEnumerator函数的for循环不是一下都遍历完了吗,怎么还能给foreach用,好蒙啊。。。yield很神奇吧?是这样的:Jon Skeet说:“迭代器模式的一个重要方面就是:不用一次返回所有数据,调用代码一次只需获取一个元素。”你可以理解为每次执行yield return都能够返回一个数据并暂停当前的状态,那暂停的状态什么时候会继续呢?在下一次调用到MoveNext的时候。什么时候会调用MoveNext?foreach执行完一次,进入下一次的时候。
如果还不是很明白,我们再来看看《c# in Depth》的经典例子:class IteratorWorkflow { static readonly string Padding = new string(' ', 30); static IEnumerable GetEnumerable() { Console.WriteLine("{0}Start of GetEnumerator()", Padding); for (int i = 0; i < 3; i++) { Console.WriteLine("{0}About to yield {1}", Padding, i); yield return i; Console.WriteLine("{0}After yield", Padding); } Console.WriteLine("{0}Yielding final value", Padding); yield return -1; Console.WriteLine("{0}End of GetEnumerator()", Padding); } public static void Main() { IEnumerable iterable = GetEnumerable(); IEnumerator iterator = iterable.GetEnumerator(); Console.WriteLine("Starting to iterate"); while (true) { Console.WriteLine("Calling MoveNext()..."); bool result = iterator.MoveNext(); Console.WriteLine("... MoveNext result={0}", result); if (!result) { break; } Console.WriteLine("Fetching Current..."); Console.WriteLine("... Current result={0}", iterator.Current); } } }输出的结果为: 我相信,如果你有对照着这个例子认真分析一遍的话,应该就能掌握yield这个知识点了,如果还不清楚,代码Copy下来,自己跑一遍~~
最后有几个知识点总结归纳一下:
1·在遇到yield break或者返回IEnumerator的函数体结束前,不管yield return 的值为多少,MoveNext都是会返回True。
2·在第一次调用MoveNext之前,返回IEnumerable的代码都不会执行,即使你有主动去调用它。
3·执行到yield return的地方,代码就暂停了,并返回相应的值,在下一次调用MoveNext时,从上次暂停的地方继续执行。
4·yield return 代码不能放入try...catch块中,但是能放入try...finally块中。
更多unity2018的功能介绍请到paws3d爪爪学院查找。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
好程序员分享JavaScript事件委托代理和函数封装详解
好程序员分享JavaScript事件委托代理和函数封装详解,JavaScript高级程序设计上讲:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。下面我们一起看看事件代理相关介绍。 当用事件委托的时候,根本就不需要去遍历元素的子节点,只需要给父级元素添加事件就好了,其他的都是在js里面的执行,这样可以大大的减少dom操作,这才是事件委托的精髓所在。 基本概念 事件委托,通俗地来讲,就是把一个元素响应事件(click、keydown......)的函数委托到另一个元素; 一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。 举个例子,比如一个宿舍的同学同时快递到了,一种方法就是他们都傻傻地一个个去领取,还有一种方法就是把这件事情委托给宿舍长,让一个人出去拿好所有快递,然后再根据收件人一一分发给每个宿舍同学; 在这里,取快递就是一个事件,每个同学指的是需要响应事件的 DOM 元素,而出去统一领取快递的宿舍长...
- 下一篇
Python中的运算符与表达式
你所编写的大多数语句(逻辑行)都包含了Python表达式(Expressions)。一个表达式的简单例子便是 2+3。表达式可以拆分成运算符(Operators)与操作数(Operands)。运算符(Operators)是进行某些操作,并且可以用诸如 + 等符号或特殊关键词加以表达的功能。运算符需要一些数据来进行操作,这些数据就被称作操作数(Operands)。在上面的例子中 2 和 3 就是操作数。运算符 接下来我们将简要了解各类Python运算符及它们的用法。要记得你可以随时在解释器中对给出的案例里的表达式进行求值。例如要想测试表达式 2+3,则可以使用交互式 Python 解释器提示符: >>> 2 + 3 5 >>> 3 * 5 15 >>> 下面是可用运算符的速览:+(加) 两个对象相加。3+5 则输出 8。'a' + 'b' 则输出 'ab'。-(减) 从一个数中减去另一个数,如果第一个操作数不存在,则假定为零。-5.2 将输出一个负数,50 - 24 输出 26。*(乘) 给出两个数的乘积,或返回字符串重复指定次数后的...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Linux系统CentOS6、CentOS7手动修改IP地址
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS关闭SELinux安全模块
- CentOS7设置SWAP分区,小内存服务器的救世主