• C#延迟执行


     借鉴于该篇博客:http://kb.cnblogs.com/page/42581/

    先看两个方法

    public class YieldClasses
        {
    
            public static IEnumerable<int> WithNoYied()
            {
                List<int> list = new List<int>();
    
                for (int i = 0; i < 100; i++)
                {
                    Console.Write(i.ToString());
                    list.Add(i);
                }
                return list;
    
            }
    
          public static IEnumerable<int> WithYied()
            {
                for (int i = 0; i < 100; i++)
                {
                    Console.WriteLine(i.ToString());
                    yield return i;
                }
            }
            
        }
    

      分别用执行他们接过是怎样

        YieldClasses.WithNoYied();

         YieldClasses.WithYied();??????

    令人惊讶的是:第一个函数跟预期的一样,从1输到99。而第二个函数却什么 也没有输出!这是神马回事???

    我们再foreach中迭代两个函数

     foreach (var item in YieldClasses.WithNoYied())
                {
                    Console.Write(item.ToString()+"-");
                }
    结果:
    withYied的结果

    withnoYied的结果:

    1233456789101112.。。。。。。。0-1-2-3-4-5-6-7-8-9-10.。。。(突然截图不行了)

    这是神马回事呢?

    从显示的结果可以看出,在WithNoYied中,是先循环执行完函数中的console.write,再执行main中的write

    而在WithYied中,是循环一次,先执行函数中的wirte,再执行main中的write,再循环下一次。。。。

    这就是症结所在!明之:yield的延迟执行!

    http://kb.cnblogs.com/page/42581/2/ 这里有用IL的方法分析。我就简单的总结以下吧:

    延迟计算(Lazy evaluation或delayed evaluation)在Wiki上可以找到它的解释:将计算延迟,直到需要这个计算的结果的时候才计算,这样就可以因为避免一些不必要的计算而改进性能,在合成一些表达式时候还可以避免一些不必要的条件,因为这个时候其他计算都已经完成了,所有的条件都已经明确了,有的根本不可达的条件可以不用管了。反正就是好处很多了。

    延迟计算来源自函数式编程,在函数式编程里,将函数作为参数来传递,你想呀,如果这个函数一传递就被计算了,那还搞什么搞,如果你使用了延迟计算,表达式在没有使用的时候是不会被计算的,比如有这样一个应用:x=expression,将这个表达式赋给x变量,但是如果x没有在别的地方使用的话这个表达式是不会被计算的,在这之前x里装的是这个表达式。

    延迟计算在linq中的应用:

     看看下面的方法:

    public static void  LinqTest()
            {
                int[] intArray = {1,3,6,5,9,34 };
                int a=0;
     
                var result = from item in intArray
                             select item;
                if(a==0)
                {
                    foreach(int i in result)
                    {
                        Console.Write(i);
                    }
                }
            
            }
    这样的方法,如果a==0,那会输出结果(先执行linq操作),但如果a!=0,那么linq操作就不执行!

    result是一个实现了IEnumerable接口的类(在Linq里,所有实现了IEnumerable接口的类都被称作sequence),对它的foreach或者while的访问必须通过它对应的IEnumerator的MoveNext()方法,如果我们把一些耗时的或者需要延迟的操作放在MoveNext()里面,那么只有等到MoveNext()被访问,也就是result被使用的时候那些操作才会执行,而给result赋值啊,传递啊,什么的,那些耗时的操作都没有被执行。

    如果上面这段代码,最后由于state小于0,而对result没有任何需求了,在Linq里返回的结果都是IEnumerable的,如果这里没有使用延迟计算,那那个Linq表达式不就白运算了么?如果是Linq to Objects还稍微好点,如果是Linq to SQL,而且那个数据库表又很大,真是得不偿失啊,所以微软想到了这点,这里使用了延迟计算,只有等到程序别的地方使用了result才会计算这里的Linq表达式的值的,这样Linq的性能也比以前提高了不少,而且Linq to SQL最后还是要生成SQL语句的,对于SQL语句的生成来说,如果将生成延迟,那么一些条件就先确定好了,生成SQL语句的时候就可以更精练了。还有,由于MoveNext()是一步步执行的,循环一次执行一次,所以如果有这种情况:我们遍历一次判断一下,不满足我们的条件了我们就退出,如果有一万个元素需要遍历,当遍历到第二个的时候就不满足条件了,这个时候我们就可就此退出,后面那么多元素实际上都没处理呢,那些元素也没有被加载到内存中来。

    延迟计算还有很多惟妙惟肖的特质,也许以后你也可以按照这种方式来编程了呢。写到这里我突然想到了Command模式,Command模式将方法封装成类,Command对象在传递等时候是不会执行任何东西的,只有调用它内部那个方法他才会执行,这样我们就可以把命令到处发,还可以压栈啊等等而不担心在传递过程中Command被处理了,也许这也算是一种延迟计算吧。

    本文也只是很浅的谈了一下延迟计算的东西,从这里还可以牵扯到并发编程模型和协同程序等更多内容,由于本人才疏学浅,所以只能介绍到这个地步了,上面一些说法也是我个人理解,肯定有很多不妥地方,欢迎大家拍砖。

    
    
  • 相关阅读:
    VPython—旋转坐标系
    分布式锁简单入门以及三种实现方式介绍
    win10 64bit安装redis及redis desktop manager的方法
    Kafka史上最详细原理总结
    idea常用快捷键
    十大Intellij IDEA快捷键
    Spark(一): 基本架构及原理
    Idea Live Templates代码模板
    IntelliJ IDEA 常用快捷键列表及技巧大全
    Win10 下 RabbitMQ 的 安装 配置
  • 原文地址:https://www.cnblogs.com/fjsnail/p/3232933.html
Copyright © 2020-2023  润新知