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


            本篇我们讨论for和foreach配合yield的情况。首先看如下代码以及生成的隐藏类。

            public static IEnumerable Power(int baseNumber, int highExponent)
            {
                int result = 1;
    
                for (int counter = 1; counter <= highExponent; counter++)
                {
                    result = result * baseNumber;
                    yield return result;
                }
            }

            这是一个算baseNumber的highExponet次幂的方法,在for循环里每计算一次就通过yield返回。在Reflector里,该方法是长成这样的:

            再看一下生成的<Power>d_0这个类,因为本文的目的是仿造yield关键字,所以这里仅贴出类定义,具体实现有兴趣各位自己看一下哈哈。

            那么紧接着我们就来仿造一个yield类来代替这个生成的类,去掉不必要的接口和多余的字段,干脆连状态机也拿掉,一切从简之后代码如下:

        class FakeYieldPower : IEnumerable, IEnumerator
        {
            private object current;
            private int counter = 1;
            private int result = 1;
            public int baseNumber;
            public int highExponent;
    
            public bool MoveNext()
            {
                if (this.counter > this.highExponent)
                {
                    return false;
                }
                else
                {
                    this.result *= this.baseNumber;
                    this.current = this.result;
                    this.counter++;
                    return true;
                }
            }
    
            public IEnumerator GetEnumerator()
            {
                return new FakeYieldPower { baseNumber = this.baseNumber, highExponent = this.highExponent };
            }
    
            public void Reset()
            {
                throw new NotSupportedException();
            }
    
            public object Current
            {
                get
                {
                    return this.current;
                }
            }
        }

            也许有人会问,为什么yield返回值一定要同时实现IEnumerable和IEnumerator2个接口。通过IEnumerable接口中的GetEnumerator方法,可以得到一个单纯的实现IEnumerator接口的类,这样划分更清晰,类的职责更明确。其实我也不是很肯定啊,我个人的理解是可以少生成一个隐藏类,同时可以方便地把IEnumerable和IEnumerator转来转去……哎呀,不要扔鸡蛋啊……各位我们先看使用效果……

            public static void Process()
            {
                // Output: 2 4 8 16 32 64 128 256
                foreach (int number in Power2(2, 8))
                {
                    Console.Write(number.ToString() + " ");
                }
            }
    
            public static IEnumerable Power2(int baseNumber, int highExponent)
            {
                return new FakeYieldPower { baseNumber= baseNumber, highExponent= highExponent };
            }

            经本人鉴定是好使的,最显著的变化是我把state状态给删了,这说明yield不是一定要switch case配合state弄成个状态机,里面再插2个goto语句。至于MS为什么这么做,我想是因为该类是自动生成,状态机是一个好选择。按照一定的算法能生成通用的代码。

            for循环看完之后接下来我们看一下foreach,这个其实有点蛋疼了,因为能使用foreach的对象,本身就实现了IEnumerable,再把他用yield转成IEnumerable和IEnumerator的混合体返回还是蛮奇怪的。比如:

            public IEnumerable<Person> GetPerson()
            {
                List<Person> personList = this.GetPersonList();
                foreach (var p in personList)
                {
                    yield return p;
                }
            }

            生成的代码绝对会让你产生蛋蛋的忧伤,还是伪造yield好了:

            public IEnumerable<Person> GetPersonEnumerable()
            {
                return this.GetPersonList();
            }
    
            public IEnumerator<Person> GetPersonEnumerator()
            {
                return this.GetPersonList().GetEnumerator();
            }

            别打……别打了,真的没有的可以伪造的余地啊,本来就已经都写好了啊T_T

            还是来测试一下使用效果,必须效果杠杠滴,就跟CCAV一样,都彩排好了,不OK我不放出来:

                foreach (var p in person.GetPersonEnumerable())
                {
                    Console.WriteLine(p.Name);
                }
    
                var iterator = person.GetPersonEnumerator();
                while (iterator.MoveNext())
                {
                    Console.WriteLine(iterator.Current.Name);
                }

            至此我们对yield的学习告一段落,话说yield确实省了我们不少行代码,提高了生产了。当然负面的作用就是让初学者云里雾里不明所以。

            代码下载

            

  • 相关阅读:
    Kaffeine Player:固守丰富的媒体播放器
    GIMP 2.2.15
    基于终真个常用工具
    运用 GNOME Specimen 检查字体
    LINA:让 Linux 使用法度圭表标准在 Windows 和 Mac OS X 上运转
    Openbox 3.4 公布
    DB2 9 运用拓荒(733 查验)认证指南,第 1 局部: 数据库器材与编程步伐(1)
    Wammu-挪动电话治理器
    会计人员必去十大网站(最新)
    判断一个数组的长度用 Length 还是 SizeOf ?
  • 原文地址:https://www.cnblogs.com/manupstairs/p/2817239.html
Copyright © 2020-2023  润新知