首页 > 学院 > 开发设计 > 正文

C# 基础小知识之yield 关键字

2019-11-17 02:28:16
字体:
来源:转载
供稿:网友
C# 基础小知识之yield 关键字

对于yield关键字我们首先看一下msdn的解释:

如果你在语句中使用yield关键字,则意味着它在其中出现的方法、运算符或get访问器是迭代器。通过使用yield定义迭代器,可在实现自定义集合类型的IEnumerable和IEnumerator模式时无需其他显式类(保留枚举状态的类,有关示例,请参阅IEnumerator<T>)。

yield是一个语法糖

看msdn 的解释总是让人感觉生硬难懂。其实yield关键字很好理解。首先我们对于性质有个了解。yield是一个语法糖。既然yield是在C#中的一个语法糖,那么就说明yield是对一种复杂行为的简化,就是将一段代码简化为一种简单的形式,方便我们程序员使用。

那么yield到底是对什么行为的简化。我们首先来看一下yield的使用场景。

还是来看msdn上的例子。

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Consoleapplication2{    class PRogram    {        static void Main(string[] args)        {                     foreach (int i in Power(2, 8, ""))            {                Console.Write("{0} ", i);            }            Console.ReadKey();        }        public static IEnumerable<int> Power(int number, int exponent, string s)        {            int result = 1;            for (int i = 0; i < exponent; i++)            {                result = result * number;                yield return result;            }            yield return 3;            yield return 4;            yield return 5;        }    }}

这是msdn上yield的一种使用场景。

我们首先看一下下面的Power方法。该静态方法返回一个IEnumerablel<int>类型的参数。按照我们平常的做法。应该对数据执行一定操作,然后return一个IEnumerablel<int>类型的参数。我们把Power方法改造如下:

 public static IEnumerable<int> Power(int number, int exponent, string s)        {            int result = 1;            //接口不能实例化,我们这儿new一个实现了IEnumerable接口的List            IEnumerable<int> example = new List<int>();            for (int i = 0; i < exponent; i++)            {                result = result * number;                (example as List<int>).Add(result);            }            return example;        }

这是我们平常的思路。但是这样做就有个问题。这儿要new一个List,或者任何实现了IEnumerable接口的类型。这样也太麻烦了吧。要知道IEnumerable是一个常用的返回类型。每次使用都要new一个LIst,或者其他实现了该接口的类型。与其使用其他类型,不如我们自己定制一个实现了IEnumerable接口专门用来返回IEnumerable类型的类型。我们自己定制也很麻烦。所以微软帮我们定制好了。这个类是什么,那就是yield关键字这个语法糖。

语法糖的实现(实现IEnumerable<T>接口的类)

我们来看一下yield的反编译代码。

namespace ConsoleApplication2{    using System;    using System.Collections;    using System.Collections.Generic;    using System.Diagnostics;    using System.Runtime.CompilerServices;    internal class Program    {        private static void Main(string[] args)        {            IEnumerable<int> enumerable = Power(2, 8);            Console.WriteLine("Begin to iterate the collection.");            foreach (int num in Power(2, 8))            {                Console.Write("{0} ", num);            }            Console.ReadKey();        }        public static IEnumerable<int> Power(int number, int exponent)        {            <Power>d__0 d__ = new <Power>d__0(-2);            d__.<>3__number = number;            d__.<>3__exponent = exponent;            return d__;        }        [CompilerGenerated]        private sealed class <Power>d__0 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable        {            private int <>1__state;            private int <>2__current;            public int <>3__exponent;            public int <>3__number;            private int <>l__initialThreadId;            public int <result>5__1;            public int exponent;            public int number;            [DebuggerHidden]            public <Power>d__0(int <>1__state)            {                this.<>1__state = <>1__state;                this.<>l__initialThreadId = Environment.CurrentManagedThreadId;            }            private bool MoveNext()            {                switch (this.<>1__state)                {                    case 0:                        this.<>1__state = -1;                        this.<result>5__1 = 1;                        Console.WriteLine("Begin to invoke GetItems() method");                        this.<>2__current = 3;                        this.<>1__state = 1;                        return true;                    case 1:                        this.<>1__state = -1;                        this.<>2__current = 4;                        this.<>1__state = 2;                        return true;                    case 2:                        this.<>1__state = -1;                        this.<>2__current = 5;                        this.<>1__state = 3;                        return true;                    case 3:                        this.<>1__state = -1;                        break;                }                return false;            }            [DebuggerHidden]            IEnumerator<int> IEnumerable<int>.GetEnumerator()            {                Program.<Power>d__0 d__;                if ((Environment.CurrentManagedThreadId == this.<>l__initialThreadId) && (this.<>1__state == -2))                {                    this.<>1__state = 0;                    d__ = this;                }                else                {                    d__ = new Program.<Power>d__0(0);                }                d__.number = this.<>3__number;                d__.exponent = this.<>3__exponent;                return d__;            }            [DebuggerHidden]            IEnumerator IEnumerable.GetEnumerator()            {                return this.System.Collections.Generic.IEnumerable<System.Int32>.GetEnumerator();            }            [DebuggerHidden]            void IEnumerator.Reset()            {                throw new NotSupportedException();            }            void IDisposable.Dispose()            {            }            int IEnumerator<int>.Current            {                [DebuggerHidden]                get                {                    return this.<>2__current;                }            }            object IEnumerator.Current            {                [DebuggerHidden]                get                {                    return this.<>2__current;                }            }        }    }}
View Code

反编译代码有三部分,其中程序的入口点 private static void Main(string[] args) Power方法 public static IEnumerable<int> Power(int number, int exponent) 和我们自己写的代码一样,但是反编译代码中还多了一个密封类

private sealed class <Power>d__0 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable

现在情况已经明了了。yield这个语法糖实现了一个实现IEnumerable<int>接口的类来返回我们需要到IEnumerable<int>类型的数据。

我们再看一下反编译后的Power方法

 public static IEnumerable<int> Power(int number, int exponent)        {            <Power>d__0 d__ = new <Power>d__0(-2);            d__.<>3__number = number;            d__.<>3__exponent = exponent;            return d__;        }

此时就确认,的确是使用了实现枚举接口的类来返回我们需要的数据类型。

每次yield return <expression>;就会像该类的实例中添加 一条数据。当yield break;的时候停止添加。

至此yield的用法就很清楚了。当我们需要返回IEnumerable类型的时候,直接yield返回数据就可以了。也不用new一个list,或其他类型。所以yield是一个典型的语法糖。

yield使用中的特殊情况

我们看到编译器将我们yield的数据添加到了一个集合中。Power方法在编译器中实例化了一个实现枚举接口的类型。但是我们在Power方法中写一些方法,编译器会如何处理

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace ConsoleApplication2{    class Program    {        static void Main(string[] args)        {            //这儿调用了方法。            var test = Power(2, 8, "");            Console.WriteLine("Begin to iterate the collection.");            //Display powers of 2 up to the exponent of 8:            foreach (int i in Power(2, 8, ""))            {                Console.Write("{0} ", i);
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表