• 《C#本质论》读书笔记(15)使用查询表达式的LINQ


    15.1 查询表达式的概念


    简单的查询表达式

    1. private static void ShowContextualKeywords1()  

    2. {  

    3.     IEnumerable<string> selection = from word in Keywords  

    4.                                     where !word.Contains('*')  

    5.                                     select word;  

    6.   

    7.     foreach (string keyword in selection)  

    8.     {  

    9.         Console.Write(" " + keyword);  

    10.     }  

    11. }  

    12.   

    13. private static string[] Keywords = {  

    14.                                        "abstract""add*""alias*""as""ascending*""base",  

    15.                                        "bool""break""by*""byte""case""catch""char",  

    16.                                        "checked""class""const""continue""decimal",  

    17.                                        "default""delegate""descending*""do""double",  

    18.                                        "dynamic*""else""enum""event""equals*",  

    19.                                        "explicit""extern""false""finally""fixed",  

    20.                                        "from*""float""for""foreach""get*""global*",  

    21.                                        "group*""goto""if""implicit""in""int",  

    22.                                        "into*""interface""internal""is""lock""long",  

    23.                                        "join*""let*""namespace""new""null""object",  

    24.                                        "on*""operator""orderby*""out""override",  

    25.                                        "params""partial*""private""protected""public",  

    26.                                        "readonly""ref""remove*""return""sbyte""sealed",  

    27.                                        "select*""set*""short""sizeof""stackalloc",  

    28.                                        "static""string""struct""switch""this""throw",  

    29.                                        "true""try""typeof""uint""ulong""unchecked",  

    30.                                        "unsafe""ushort""using""value*""var*""virtual",  

    31.                                        "void""volatile""where*""while""yield*"  

    32.                                    };  


    15.1.1 投射

    查询表达式输出是一个 IEnumerbale<T> IQueryable<T>集合。T数据类型是从select或者groupby子句推导。
    上例string数据类型是从 select word推导的,因为word是一个字符串。word数据类型是由from子句所指定的IEnumerbale<T>集合的类型参数(这里是Keywords)。由于Keywords是一个string数组,它实现了IEnumerbale<T>,所以word是一个字符串。
    表达式查询特定类型集合时,结果允许将数据投射成一个完全不同的类型。
    Directory.GetCurrentDirectory()  

    1. public static void Main()  

    2.  {  

    3.      List1(Directory.GetCurrentDirectory(),"*");  

    4.  }  

    5.   

    6.  static void List1(string rootDirectory, string searchPattern)  

    7.  {  

    8.      IEnumerable<FileInfo> files =  

    9.          from fileName in Directory.GetFiles(  

    10.              rootDirectory, searchPattern)  

    11.          select new FileInfo(fileName);  

    12.   

    13.      foreach (FileInfo file in files)  

    14.      {  

    15.          Console.WriteLine(".{0} ({1})",  

    16.              file.Name, file.LastWriteTime);  

    17.      }  

    18.  }  

     

     这里返回的是一个IEnumerable<FileInfo>,而不是System.IO.Directory.GetFiles()返回的IEnumerables<string>数据类型。
     
    C#3.0引入匿名类型,很大程度上就是利用像这样的“投射”功能。
    1. var files =    

    2. from fileName in Directory.GetFiles(    

    3.     rootDirectory, searchPattern)    

    4. select new FileInfo(fileName);    



    15.1.2 筛选

    where子句在垂直方向筛选集合。
    1.     IEnumerable<string> selection = from word in Keywords  

    2.                                     where !word.Contains('*')  

    3.                                     select word;  



    15.1.3 排序

    在查询表达式中对数据进行排序的是 orderby 子句。
    1. IEnumerable<string> fileNames =  

    2.     from fileName in Directory.GetFiles(  

    3.         rootDirectory, searchPattern)  

    4.     orderby (new FileInfo(fileName)).Length descending,  

    5.         fileName  

    6.     select fileName;  


    ascendingdescending是上下文关键字,分别是升序或降序排序。


    15.1.4 let子句

    下面代码与上面的代码相似。问题是FileInfo要创建两次,分别在orderby 和 select子句中创建。
    1. public static void Main()  

    2. {  

    3.     ListByFileSize2(Directory.GetCurrentDirectory(), "*");  

    4. }  

    5.   

    6. static void ListByFileSize2(  

    7.         string rootDirectory, string searchPattern)  

    8. {  

    9.     IEnumerable<FileInfo> files =  

    10.         from fileName in Directory.GetFiles(  

    11.             rootDirectory, searchPattern)  

    12.         orderby new FileInfo(fileName).Length, fileName  

    13.         select new FileInfo(fileName);  

    14.   

    15.     foreach (FileInfo file in files)  

    16.     {  

    17.         //  As simplification, current directory is  

    18.         //  assumed to be a subdirectory of  

    19.         //  rootDirectory  

    20.         string relativePath = file.FullName.Substring(  

    21.             Environment.CurrentDirectory.Length);  

    22.         Console.WriteLine(".{0}({1})",  

    23.             relativePath, file.Length);  

    24.     }  

    25. }  


    可以用let子句避免这种昂贵的开销。
    1. IEnumerable<FileInfo> files =  

    2.                 from fileName in Directory.GetFiles(  

    3.                     rootDirectory, searchPattern)  

    4.                 let file = new FileInfo(fileName)  

    5.                 orderby file.Length, fileName  

    6.                 select file;  


    let 解释
    1. let子句引入了一个新的范围变量

    2. 它容纳的表达式值可以在查询表达式剩余部分使用

    3. 可以添加任意数量的let表达式,只需要它们每一个作为一个附加的子句

    4. 放在第一个from子句之后,最后一个select/group by子句之前,加入查询即可



    15.1.5 分组

    SQL中涉及对数据项进行聚合以生成一个汇总或合计或其他聚合值。
    LINQ中表达力更强,LINQ允许将单独的项分组到一系列子集合中,还允许那些组与所查的集合中项关联
    1. private static void GroupKeywords1()  

    2. {  

    3.     IEnumerable<IGrouping<boolstring>> selection =  

    4.         from word in keyWords  

    5.         group word by word.Contains('*');  

    6.   

    7.     foreach (IGrouping<boolstring> wordGroup  

    8.         in selection)  

    9.     {  

    10.         Console.WriteLine(Environment.NewLine + "{0}:",  

    11.             wordGroup.Key ?  

    12.                 "Contextual Keywords" : "Keywords");  

    13.         foreach (string keyword in wordGroup)  

    14.         {  

    15.             Console.Write(" " +  

    16.                 (wordGroup.Key ?  

    17.                     keyword.Replace("*"null) : keyword));  

    18.         }  

    19.     }  

    20. }  



    结果


     查询结果是一系列IGrouping<bool, string>类型元素。
     
    查询生成一系列分组,将相同的bool类型键应用于组内的每个string。


    group子句后面选择一个匿名类型
    1. private static void GroupKeywords1()  

    2. {  

    3.     IEnumerable<IGrouping<boolstring>> keywordGroups =  

    4.         from word in keyWords  

    5.         group word by word.Contains('*');  

    6.   

    7.     var selection = from groups in keywordGroups  

    8.                     select new  

    9.                     {  

    10.                         IsContextualKeyword = groups.Key,  

    11.                         Items = groups  

    12.                     };  

    13.   

    14.     foreach (var wordGroup in selection)  

    15.     {  

    16.         Console.WriteLine(Environment.NewLine + "{0}:",  

    17.             wordGroup.IsContextualKeyword ?  

    18.                 "Contextual Keywords" : "Keywords");  

    19.         foreach (var keyword in wordGroup.Items)  

    20.         {  

    21.             Console.Write(" " +  

    22.                 keyword.Replace("*"null));  

    23.         }  

    24.     }  

    25. }  


     IGrouping<TKey,TElement>.Key 重命名为IsContextualKeyword,并命名了子集合属性Items。有人人为可以在匿名类型中添加一个属性来标识数据项的个数,然后,这个功能由wordGroup.Items.Count()提供。


    15.1.6 使用into进行查询延续

    上面的代码,一个现有的查询可以作为另一个查询的输入。然而,我们可以使用into通过查询延续子句来扩展任何查询。查询延续是一个语法糖,能简单的表示“创建量个查询并将第一个用作第二个的入口”。
    1. var selection =  

    2.     from word in keyWords  

    3.     group word by word.Contains('*')  

    4.         into groups  

    5.         select new  

    6.         {  

    7.             IsContextualKeyword = groups.Key,  

    8.             Items = groups  

    9.         };  


    into相当于一个管道操作符,它将第一个查询结果“管道传送”给第二个查询。用这种方式可以链接起任意数量的查询。


    15.1.7 用多个from 子句“平整”序列的序列

    经常将一个序列的序列“平整”成单个序列。一系列客户中的每个客户都可能关联一系列订单,或者一系列目录中的每个目录都关联了一系列文件。SelectMany序列操作符(14章讨论过)可以连接所有的子序列;要用查询表达式做相同的事情,可以用多个from子句
    1. var selection = from word in keyWords  

    2.     from character in word  

    3.     select character;  



     还可以用多个from子句生成笛卡尔积——几个序列所有可能的组合。
    1. var numbers = new[] {1, 2, 3};  

    2. var product = from word in keyWords  

    3.     from number in numbers  

    4.     select new {word, number};  


     

    主题:不重复的成员

    查询表达式没与专门的语法,但是,可以结合查询操作符Distict()来实现。
     typeof(Enumerable).GetMembers()返回System.Linq.Enumerable的所有成员(方法、属性等)的一个列表。但许多成员都是重载。

    15.2 查询表达式作为方法调用

    查询表达式未对CLR或CIL语言进行任何改动。由C#编译器将查询表达式转换成一系列方法调用。
    1. IEnumerable<string> selection = from word in Keywords  

    2.                                 where !word.Contains('*')  

    3.                                 select word;  


    编译之后,会转换成一个由System.Linq.Enumerable提供的IEnumerable<T>扩展方法调用
    1. IEnumerable<string> selection =   

    2.     keyWords.Where(word => word.Contains('*'));  


    如14章所述,Lambda表达式随后由编译器转换成一个带有Lambda的主体的方法。Lambda表达式的作用变成分配一个对该方法的委托。        
    每个查询表达式都能转换成方法调用,但不是每一系列的方法调用都有对应的查询表达式。例如,扩展方法TakeWhile<T>(Func<T,bool> predicate)就没有与之等价的查询表达式,这个扩展方法只要谓词返回true,就反复返回集合中的项。






  • 相关阅读:
    SQL SERVER事务处理
    设计模式之简单工厂模式(静态工厂方法)
    设计模式之工厂方法模式
    为什么静态成员变量要通过类外初始化赋值?
    c++中可以对类中私有成员中的静态变量初始化吗?
    C++中的虚函数(virtual function)
    C++ 基础学习笔记(2)函数(测试题)
    c++中子对象的初始化可在复合类的构造函数的函数体内进行吗?还是子对象的初始化只能在初始化列表中进行?
    C++学习笔记(五)虚函数表解析(转)
    看过的书籍(转)
  • 原文地址:https://www.cnblogs.com/tangge/p/6084871.html
Copyright © 2020-2023  润新知