造假造上瘾——仿造yield关键字(一)

        本篇会简单的介绍yield关键字,通过yield关键字返回的类型,以及Reflector反编译的结果来分析yield关键字。最后给出一个仿造的方法。

        首先我们看一下yield的用法,他的返回类型返回类型必须是 IEnumerableIEnumerable<T>IEnumerator或 IEnumerator<T>,这意味着yield生成的这个对象必须同时实现IEnumerableIEnumerator这2个接口。

    class Program

    {

        public static IEnumerable Easy1()

        {

            yield return 1;

        }



        static void Main(string[] args)

        {

            foreach (var item in Program.Easy1())

            {

                Console.WriteLine(item);

            }

        }

    }

         上述代码通过Reflector反编译的结果如下:

internal class Program

{

    // Methods

    public Program();

    public static IEnumerable Easy1();

    private static void Main(string[] args);



    // Nested Types

    [CompilerGenerated]

    private sealed class <Easy1>d__0 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IEnumerator, IDisposable

    {

        // Fields

        private int <>1__state;

        private object <>2__current;

        private int <>l__initialThreadId;



        // Methods

        [DebuggerHidden]

        public <Easy1>d__0(int <>1__state);

        private bool MoveNext();

        [DebuggerHidden]

        IEnumerator<object> IEnumerable<object>.GetEnumerator();

        [DebuggerHidden]

        IEnumerator IEnumerable.GetEnumerator();

        [DebuggerHidden]

        void IEnumerator.Reset();

        void IDisposable.Dispose();



        // Properties

        object IEnumerator<object>.Current { [DebuggerHidden] get; }

        object IEnumerator.Current { [DebuggerHidden] get; }

    }

}



 

        我们可以看到,这里编译器为我们自动生成了一个叫<Easy1>d_0的类,同时我们可以看到Easy1方法返回的就是该类型:

    public static IEnumerable Easy1()

    {

        return new <Easy1>d__0(-2);

    }

        <Easy1>d_0经过简化的代码如下,为了能实际使用,我把类名换成了FakeYield:

    class FakeYield : IEnumerable, IEnumerator

    {

        private int state;

        private object current;



        public FakeYield(int state)

        {

            this.state = state;

        }



        public bool MoveNext()

        {

            switch (this.state)

            {

                case 0:

                    this.state = -1;

                    this.current = 1;

                    this.state = 1;

                    return true;



                case 1:

                    this.state = -1;

                    break;

            }

            return false;

        }



        IEnumerator IEnumerable.GetEnumerator()

        {

            if (this.state == -2)

            {

                this.state = 0;

                return this;

            }

            return new FakeYield(0);

        }



        void IEnumerator.Reset()

        {

            throw new NotSupportedException();

        }



        object IEnumerator.Current

        {

            get

            {

                return this.current;

            }

        }

    }

        这里说明一下state不同值的含义,-2表示首次初始化该类,由外部调用构造函数时赋值。0表示初始状态,一切就绪。-1表示一次操作取值完成,1在这个类里是迭代完成的状态。这里我们省略了泛型接口、线程ID initIalThreadId以及IDispose接口。感兴趣的可以自己补完。

        如果对迭代器模式有所了解的话,可以看出这里MoveNext方法仅仅是返回了一次true,给current赋值1,也没干啥正经事。不过这正是Easy1方法的忠实体现……

        那我们再来看一下稍微复杂一些的yield用法:

    class Person

    {

        public string Name { get; set; }



        public int Age { get; set; }



        public IEnumerable<Person> GetPersons()

        {

            yield return new Person { Age = 27, Name = "Leo" };

            yield return new Person { Age = 26, Name = "Echo" };

            yield return new Person { Age = 25, Name = "Peter" };

        }

    }

        和之前比有2点不同,首先GetPersons是一个实例方法,其次返回值是泛型的IEnumerable<Person>。体现在代码中的话,实例方法在该迭代类中会生成一个Person的自引用,在调用该迭代类时传递Person实例对象进去。由于Person对象不在是基本类型,类中产生了一些额外的字段,当然这是因为自动生成的原因,如果人肉去写这个类,自然能够优化。

        private sealed class <GetPersons>d__3 : IEnumerable<Person>, IEnumerable, IEnumerator<Person>, IEnumerator, IDisposable

        {

            private int <>1__state;

            private Person <>2__current;

            public Person <>4__this;

            public Person <>g__initLocal0;

            public Person <>g__initLocal1;

            public Person <>g__initLocal2;

            private int <>l__initialThreadId;



            [DebuggerHidden]

            public <GetPersons>d__3(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.<>g__initLocal0 = new Person();

                        this.<>g__initLocal0.Age = 0x1b;

                        this.<>g__initLocal0.Name = "Leo";

                        this.<>2__current = this.<>g__initLocal0;

                        this.<>1__state = 1;

                        return true;



                    case 1:

                        this.<>1__state = -1;

                        this.<>g__initLocal1 = new Person();

                        this.<>g__initLocal1.Age = 0x1a;

                        this.<>g__initLocal1.Name = "Echo";

                        this.<>2__current = this.<>g__initLocal1;

                        this.<>1__state = 2;

                        return true;



                    case 2:

                        this.<>1__state = -1;

                        this.<>g__initLocal2 = new Person();

                        this.<>g__initLocal2.Age = 0x19;

                        this.<>g__initLocal2.Name = "Peter";

                        this.<>2__current = this.<>g__initLocal2;

                        this.<>1__state = 3;

                        return true;



                    case 3:

                        this.<>1__state = -1;

                        break;

                }

                return false;

            }



            [DebuggerHidden]

            IEnumerator<Person> IEnumerable<Person>.GetEnumerator()

            {

                if ((Environment.CurrentManagedThreadId == this.<>l__initialThreadId) && (this.<>1__state == -2))

                {

                    this.<>1__state = 0;

                    return this;

                }

                Person.<GetPersons>d__3 d__ = new Person.<GetPersons>d__3(0);

                d__.<>4__this = this.<>4__this;

                return d__;

            }



            [DebuggerHidden]

            IEnumerator IEnumerable.GetEnumerator()

            {

                return this.System.Collections.Generic.IEnumerable<YieldTest.Person>.GetEnumerator();

            }



            [DebuggerHidden]

            void IEnumerator.Reset()

            {

                throw new NotSupportedException();

            }



            void IDisposable.Dispose()

            {

            }



            Person IEnumerator<Person>.Current

            {

                [DebuggerHidden]

                get

                {

                    return this.<>2__current;

                }

            }



            object IEnumerator.Current

            {

                [DebuggerHidden]

                get

                {

                    return this.<>2__current;

                }

            }

        }

         在这个类中,仍然有一些值得注意的地方。首先是initialThreadId,我对该字段不是很明白,没想清楚该自动生成的类会在多线程中如何使用,还请各位指点迷津。其次是Reset方法,不支持的理由我认为是在自动生成的类中,该方法永远不会被用到。最后还有Dispose方法,在Person类没有引用非托管资源的情况下,Dispose是不需要做任何事情的,但如果引用了需要释放的资源,比如打开了文件,就应该通过该方法来关闭文件。

         本文是yield关键字的第一篇,简单的讨论了yield最常见用法的生成类。下篇我们将查看通过for和foreach配合yield返回时生成的类。

 

 

你可能感兴趣的:(yield)