• 造假造上瘾——仿造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返回时生成的类。

  • 相关阅读:
    【JAVA笔记——道】JAVA对象销毁
    【JAVA笔记——道】并发编程CAS算法
    httpClientUtil的get请求
    python基础 day11 下 ORM介绍 sqlalchemy安装 sqlalchemy基本使用 多外键关联 多对多关系 表结构设计作业
    python基础 day11 上 数据库介绍 mysql 数据库安装使用 mysql管理 mysql 数据类型 常用mysql命令 事务 索引 python 操作mysql ORM sqlachemy学习
    Python基础 Day10 Gevent协程 SelectPollEpoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitMQ队列 RedisMemcached缓存 Paramiko SSH Twsited网络框架
    python基础 day9 进程、与线程区别 python GIL全局解释器锁 线程 进程
    python基础 day8 Socket语法及相关 SocketServer实现多并发
    python基础 day7 面向对象高级语法部分 异常处理 异常处理 Socket开发基础
    python基础 day6 面向对象的特性:封装、继承、多态 类、方法、
  • 原文地址:https://www.cnblogs.com/manupstairs/p/2805536.html
Copyright © 2020-2023  润新知