linq中延迟执行

linq中延迟执行

LINQ执行过程的一个重要特征是延迟执行,Linq的大多数查询运算符并不是在构建的时候就立即执行,所有标准查询运算符均为延时执行,但是有的运算符不支持延时执行的机制,而是立即执行,如Count 、ToAarry、toLookup等。

一、LINQ延迟执行优点

1、LinQ语法,只是构造了“查询语句”,真正执行这种语句的是IEnumerator<T>里的GetEnumerator()方法,因此,当调用GetEnumerator(),就真正执行一次LinQ获取数据,多次调用GetEnumerator(),就多次执行LinQ。我们可以中途修改LinQ(即修改了“查询语句”),下次执行GetEnumerator()方法的结果就会相应地改变了。

例如

  • C# 代码   复制
  • 
                List<Employee> emps = new List<Employee>() 
                 {
                     new Employee(){ id="1001", name="ivan", dept="isd", tel="1021"},
                     new Employee(){ id="1002", name="ivan", dept="isd", tel="1021"},
                 };
    
                 var query = from e in emps
                             select e;
    
                 emps.Add(new Employee(){ id="1003", name="ivan", dept="isd", tel="1021"});
    
                 Console.WriteLine(query.Count());  //输出结果: 3
    
    		
  • 2、可以为相同的容器多次应用相同的Linq查询,而始终获得最新的结果。

    3、可以使用Linq延迟执行将额外信息的检索操作延迟到你确实需要检索它们时再进行。

    二、linq延迟执行的影响

    1、重复执行

    当我们重复遍历查询结果时,查询会被重复执行

  •  
  • C# 代码   复制
  • 
    static void TestReevaluation()
            {
                var numbers = new List<int>() { 1, 2 };
     
                IEnumerable<int> query = numbers.Select(n => n * 10);   // Build query
                foreach (int n in query) Console.Write(n + "|");        // 10|20|
     
                numbers.Clear();
                foreach (int n in query) Console.Write(n + "|");        // <nothing>
            }
    
    		
  • 有时候,重复执行对我们说可不是一个优点,理由如下

    (1)、当我们需要在某一个给定的点保存查询的结果时。

    (2)、有些查询比较耗时,比如在对一个非常大的sequence进行查询或者从远程数据库获取数据时,为了性能考量,我们并不希望一个查询会被反复执行。

    这个时候,我们就可以利用转换运算符,比如ToArray、ToList来避开重复执行,ToArray把查询结果保存至一个Array,而ToList把结果保存至泛型List<>

  •  
  • C# 代码   复制
  • 
    static void TestDefeatReevaluation()
            {
                var numbers = new List<int>() { 1, 2 };
     
                List<int> timesTen = numbers
                    .Select(n => n * 10)
                    .ToList();   // Executes immediately into a List<int>
     
                numbers.Clear();
                Console.Write(timesTen.Count);  // Still 2
            }
    
    		
  • 2、变量捕获

    如果查询的lambda表达式引用了程序的局部变量时,查询会在执行时对变量进行捕获。这意味着,如果在查询定义之后改变了该变量的值,那么查询结果也会随之改变。

  •  
  • C# 代码   复制
  • 
    static void TestCapturedVariable()
            {
                int[] numbers = { 1, 2 };
    
                int factor = 10;
                IEnumerable<int> query = numbers.Select(n => n * factor);
                factor = 20;
                foreach (int n in query)
                    Console.Write(n + "|");     // 20|40|
            }
    
    		
  • 三、如何让Linq查询立即执行呢

    Enumerable定义了诸如ToArray<T>()、ToDictionary<TSource, TKey>()、ToList<T>()在内的许多扩展方法,它们允许我们以强类型容器来捕获Linq查询结果。

    由于每调用一次GetEnumerator(),就执行一次LinQ,在“查询语句”不变的情况下,就会出现多次重复的查询操作(结果一样),这种情况下,有必要使用ToList(),ToArray()这样的方法把结果固定下来(其实也就是调用GetEnumerator方法返回结果来装载List,Array),然后操作相应的List,Array,避免重复的查询操作。

  • 例如
  •  
  •  
  • C# 代码   复制
  • 
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace TestVar
    {
        class Program
        {
            static void Main(string[] args)
            {
                int[] intAry = new int[] { 1, 2, 3, 4, 5, 6 };            //使用强类型立即获取查询结果
                List<int> intList = (from x in intAry
                                     where x % 2 == 0
                                     select x).ToList();            //输出查询结果                        foreach (int v in intList)
                {
                    Console.Write(v.ToString() + " ");
                }
                Console.ReadKey();
            }
        }
    }
    
    			
  • 标签: