• Linq学习之路(04) Linq基础揭秘


    本系列的前两篇文章主要讲了C#3.0引入的新特性,也正是这些新特性让Linq成为可能。我先总结一下前面的知识点,我简单的列举出来:

    ■ Implicitly typed local variables:隐式类型局部变量
    ■ Object initializers:对象初始化器
    ■ Lambda expressions:Lambda表达式
    ■ Extension methods:扩展方法
    ■ Anonymous types:匿名类型

    我简单的画了一张图来描述他们在Linq中扮演的角色:

    这里我为什么又拿出来呢,因为在接下来的篇幅中,我们总会用到上面这些知识点,因为他们太重要了。好了,我们进入今天的主题:

    这篇文章主要讲三个点:

    1. sequences
    2. query operators
    3. query expressions

    这里用英文原句来介绍,因为翻译成中文,味道就变了。

    好,开始我们今天的第一个知识点:

    一:Sequences

    首先我们要搞清楚,什么类型的sequence才能被Linq查询,通过我们之前的例子来说事:

    var processes = Process.GetProcesses()
        .Where(process => process.WorkingSet64 >= 1024 * 1024 * 10)
        .OrderByDescending(process => process.WorkingSet64)
        .Select(process => new
        {
            process.Id,
            Name = process.ProcessName,
            Memory = process.WorkingSet64
        });

    这段代码中,GetProcesses()方法返回的是一组Process对象的数组。在.net中,array实现了IEnumerable<T>的泛型接口,而GetProcesses方法是定义在System.Diagnostics.Process的类中,Process类实现了IEnumerable<Process>接口,所以我们暂时得到的结论是,若想使用linq query,那么我们的对象就必须实现IEnumerable<T>接口。IEnumerable<T>接口非常重要,上例中我们用到的Where、OrderByDescending、Select等扩展方法都是使用Process类型的对象作为参数。

    另外补充一点,Linq还有一个重要的特性就是Deferred query execution(延时执行),也就是说我们通过一些linq query expression或linq query operation操作获得的sequence,并没有立即到objects或数据库中把这些对象拿出来,而是等我们真正用到的时候才取出来,有个最典型的应用就是“按需加载”,对我们有利用价值的东西我们取出来,没用到的就不取,这样会降低我们程序的压力,提高性能。这里我通过一个简单的例子来说明一下延时加载:

    首先,我们不用linq查询来实现输出一个整型数组里每个元素的平方:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 
     6 namespace DeferredQueryExecution
     7 {
     8     class Program
     9     {
    10         static void Main(string[] args)
    11         {
    12             int[] nums = { 1, 2, 3 };
    13 
    14             double[] query = new double[3];
    15 
    16             //给query赋值
    17             for (int i = 0, len = nums.Length; i < len; i++)
    18             {
    19                 query[i] = Square(nums[i]);
    20             }
    21 
    22             foreach (var n in query)
    23             {
    24                 Console.WriteLine(n);
    25             }
    26 
    27             Console.ReadKey();
    28         }
    29 
    30         /// <summary>
    31         /// 计算num平方的方法
    32         /// </summary>
    33         /// <param name="num"></param>
    34         /// <returns></returns>
    35         static double Square(double num)
    36         {
    37             Console.WriteLine("计算平方值( " + num + " )...");
    38             return Math.Pow(num, 2);
    39         }
    40     }
    41 }
    普通查询

    输出结果:

    再来看看用Linq查询的示例:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 
     6 namespace DeferredQueryExecution
     7 {
     8     class Program
     9     {
    10         static void Main(string[] args)
    11         {
    12             int[] nums = { 1, 2, 3 };
    13 
    14             var query =
    15                     from num in nums
    16                     select Square(num); 
    17 
    18             foreach (var n in query)
    19             {
    20                 Console.WriteLine(n);
    21             }
    22 
    23             Console.ReadKey();
    24         }
    25 
    26         /// <summary>
    27         /// 计算num平方的方法
    28         /// </summary>
    29         /// <param name="num"></param>
    30         /// <returns></returns>
    31         static double Square(double num)
    32         {
    33             Console.WriteLine("计算平方值( " + num + " )...");
    34             return Math.Pow(num, 2);
    35         }
    36     }
    37 }
    Linq查询

    输出结果:

    这里,我们可以很清楚的看出,使用linq查询并没有立即执行,而是当我们遍历query结果的时候才会真正的执行,这就是延时执行。

    好,进入今天的第二个重点:query operations

    二:Query Operations

    什么是query operations?其实query operations就是IEnummerable<T>中给我定义的一些对于类型T的扩展方法,我列举一下常用的query operations。

     
    Filtering OfType,Where
    Projection Select,SelectMany
    Partioning Skip,SkipWhile,Take,TakeWhie
    Join   GroupJoin,Join
    Concatenation Concate
    Ordering OrderBy,OrderByDescending,Reverse,ThenBy,ThenByDescending
    Grouping GroupBy,ToLookup
    Set Distinct,Except,Intersect,Union
    Conversion AsEnumerable,AsQueryable,Cast,ToArray,ToDictionary,ToList
    Equality SequenceEqual
    Element ElementAt,ElementAtOrDefault,First,FirstOrDefault,Last,LastOrDefault,Single,SingleOrDefault
    Generation DefaultIfEmpty,Empty,Range,Repeat
    Quantifiers All,Any,Contains
    Aggregation Aggregate,Average,Count,LongCount,Max,Min,Sum

    通过这些扩展方法,我们很容易对sequences进行一系列的操作,这篇文章中不会一一对这些方法进行介绍,但是后续文章中或多或少都会涉及到这些方法。其实上文中我们已经用到了一些query operation,例如Where、Select和OrderByDescending,根据这些方法的名字我们大概都能知道他们的功能。Where其实起到一个过滤的作用,把满足条件的对象筛选出来,OrderBy和OrderByDescending方法是对Select取出来的sequence按照传进去的参数进行排序。我们可以把这些方法看作是工厂中的一系列的流水线,将original的sequences输进去进行加工,然后得到我们需要的sequences,这里我贴出一张图方便大家理解:

    三:Query expressions

    Linq还为我们提供了第二种查询方法,就是query expression的方式,在某些情况下可能query operation并不是显得那么清楚,对于熟悉T-SQL的朋友们来说,query expression可能更容易理解,本人也偏向于query expression这种方式,但是有些时候,有些query operation也不能被很好的转换为query expression,好了,这些概念型的东西我就不说了,我也说不好,我们直接看代码吧,还是上文中的例子,下面我用query expression的方法实现:

    var processes =
        from process in Process.GetProcesses()
        where process.WorkingSet64 >= 1024 * 1024 * 10
        orderby process.WorkingSet64 descending
        select new
            {
                process.Id,
                Name = process.ProcessName,
                Memory = process.WorkingSet64
            };

    哈哈,这样看起来是不是更简单呢,更偏向于我们的思维呢?

  • 相关阅读:
    对list集合中的对象进行排序(转载)
    关键字的作用
    CocoaPods的 安装 /卸载/升级
    block基本使用和底层
    程序启动 - 类调用的方法
    成员变量修饰词的作用
    宏(define)与常量(const)
    iOS
    监听网络状态
    nil、Nil、NULL与NSNull的区别及应用
  • 原文地址:https://www.cnblogs.com/ARMdong/p/3085427.html
Copyright © 2020-2023  润新知