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

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

        首先我们看一下yield的用法,他的返回类型返回类型必须是 IEnumerable、IEnumerable、IEnumerator或 IEnumerator,这意味着yield生成的这个对象必须同时实现IEnumerable和IEnumerator这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 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 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; }
    }
}

 

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

    public static IEnumerable Easy1()
    {
        return new d__0(-2);
    }

        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 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对象不在是基本类型,类中产生了一些额外的字段,当然这是因为自动生成的原因,如果人肉去写这个类,自然能够优化。

        private sealed class d__3 : IEnumerable, IEnumerable, IEnumerator, 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 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 IEnumerable.GetEnumerator()
            {
                if ((Environment.CurrentManagedThreadId == this.<>l__initialThreadId) && (this.<>1__state == -2))
                {
                    this.<>1__state = 0;
                    return this;
                }
                Person.d__3 d__ = new Person.d__3(0);
                d__.<>4__this = this.<>4__this;
                return d__;
            }

            [DebuggerHidden]
            IEnumerator IEnumerable.GetEnumerator()
            {
                return this.System.Collections.Generic.IEnumerable.GetEnumerator();
            }

            [DebuggerHidden]
            void IEnumerator.Reset()
            {
                throw new NotSupportedException();
            }

            void IDisposable.Dispose()
            {
            }

            Person IEnumerator.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关键字(一))