C#笔记(LINQ)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38801354/article/details/79661017

LINQ 代表 语言集成查询,它是 .NET 框架的扩展,使用 LINQ 轻松地从 数据库程序对象的集合 以及 XML文档 中查询数据。
对于每一种数据源类型,其背后一定有根据该数据源类型实现 LINQ 的代码模块,这些代码模块叫做 LINQ提供程序

1 LINQ概述

1.1 方法语法和查询语法

在使用LINQ查询是可以组合使用两种形式的语法:方法语法查询语法

  • 方法语法:使用标准的方法调用,这些方法是一组叫做 标准查询运算符 的方法
  • 查询语法:与 SQL 语法类似,使用 查询表达式 形式书写
  • 查询语法是 声明式,它描述的是你想返回的数据,但没有指明如何执行
  • 方法语法是 命令式,它指明了查询方法调用的顺序
  • 最终,编译器会将使用查询语法(查询表达式)翻译为方法调用的形式(标准查询运算符)

1.2 查询变量

LINQ查询可以返回两种类型的结果: 枚举标量(单一值)。
查询表达式 的等号左边的变量叫做 查询变量
使用查询变量是,要注意查询执行的时间:

  • 查询表达式 返回枚举时,查询会一直等到处理枚举是才会执行,即此时的查询变量不会包含查询的结果
    • 如果枚举被处理多次,那么查询就会被执行多次,即如果在查询执行之前数据有变动,则查询会使用最新的数据
  • 如果查询表达式返回的是标量,查询会立即执行,并把结果保存在查询变量中。

2 查询语法

2.1 查询表达式的结构

查询表达式由 from 子句查询主体 组成,其中 from 子句和查询体中的 select 子句这两部分是必须的。

2.1.1 from 子句

from 子句指定了要作为数据源使用的数据集合,它引入了迭代变量,语法:

from Type Item in Items

LINQ的from 子句和 foreach 语句主要有以下不同:

  • foreach 语句 命令式 地指定了要从第一个到最后一个按顺序地访问集合中的项,而 from 子句则是声明式的规定集合中的每个项都要被访问,但并没有假定以什么的顺序
  • foreach 语句在遇到代码时就执行其主体,而 from 子句则等到程序控制流遇到访问查询变量的语句时,才会执行

2.1.2 join 子句

LINQ中的 join 子句接受两个集合然后创建一个新的集合,新的集合中每个元素包含两个原始集合中的原始成员。语法:

var query = from s in students
            join c in studentsInCoures on s.StID equals c.StID
            where c.CourseName == "History"
            select s.LastName 

注意 join 语句必须使用上下文关键字 equals ,而不能使用 == 运算符

2.1.3 from..let..where 子句

可选的 from..let..where 子句是 *查询主体 的第一部分,可以由任意数量的3个子句组合 from 子句、let 子句和 where 子句组成(也就是说from..let..where 不是一定要三个子句同时组成)。

2.1.3.1 from 子句

查询表达式从必需from 子句开始,后面跟得是 查询主体。而查询主体本身有可以从任何数量的其他 from 子句开始,但所有的 from 子句的语法和含义都是一样的。

2.1.3.2 let 子句

let 子句接受一个表达式的运算并把它赋值给一个需要在其他运算中使用的标识符。eg:

var someInts = from a in groupA
               from b in groupB
               let sum = a + b
               where sum == 12
               select new {a, b, sum}

2.1.3.3 where 子句

where 子句根据之后的运算来去除不符合指定条件的项。只要是在 from .. let .. where 部分中,查询表达式可以有任意多个 where 子句。

var someInts = from int a in groupA
               from int b in groupB
               let sum = a + b
               where sum >= 11
               where a == 4
               select new {a, b, sum};

2.1.4 orderby 子句

orderby 子句接受一个表达式并根据表达式按顺序返回结果项。
orderby 子句的默认排序是升序,可以使用 ascendingdescending 关键字显示地设置排序为升序还是降序。
可以有多个 orderby 子句,使用 , 逗号分隔,eg:

var query = from student in students
            orderby student.Age descending
            select student

2.1.5 select..group 子句

select..group.. 字句由 select 字句和 group..by.. 字句组成,select..group 字句之前的字句 指定了数据源要选择的对象,而select 字句则指定了所选对象的哪些部分要被 select,其中的group..by.. 字句是可选的,它用来指定选择了的项如何分组,二者可以单独使用

2.1.5.1 匿名类型

select 字句选择的查询结果可以由以下内容组成:

  • 原始集合的项
  • 项某些的字段
  • 匿名类型

创建 匿名类型使用类似 对象初始化语句 一样的形式,都是没有类名或构造函数,eg:

// 匿名对象初始化语句
new {FieldProp = InitExpr, FieldProp = InitExpr, ...}

// 匿名类型数组
new[] {
    {FieldProp = InitExpr, FieldProp = InitExpr, ...},
    {FieldProp = InitExpr, FieldProp = InitExpr, ...}
}

要注意的是:

  • 匿名类型只能与局部变量配合使用,不能用于类成员
  • 由于匿名类型没有类型名称,所有必须使用 var 关键字
  • 除了对象初始化语句的普通的赋值形式,匿名类型的的对象初始化语句还有两种 赋值形式简单标识符成员访问表达式,这两种形式叫做投影初始化语句,eg:
class Other
{
    static public string Name = 'seiei';
}
class Program
{
    static void Main()
    {
        string Major = "History";
        var student = new {Age=19, Major, Other.Name}; // 第二种为简单标识符,第三种为成员访问表达式
    }
}

2.1.5.2 group 字句

group 字句可以把 select 的对象根据一些标准进行分组,作为分组依据的属性叫做 键(Key),eg:

var query = from student in students
            group student by student.Major;

foreach (var s in query)
{
    Console.log("{0}", s.Key); // 分组键,Key值
    foreach (var t in s)
    {
        Console.log("{0}, {1}", t.Name, t.FName);
    }
}

2.1.6 into 字句

into 字句是 查询延续字句,它可以接受查询的一部分结果并赋予一个名字,从而在查询的另一个部分中使用。eg:

var someInts = from a in groupA
               join b in groupB on a equals b
               into groupAandB
               from c in groupAandB
               select c;

3 方法语法

3.1 标准查询运算符

标准查询运算符有一组API组成,它能查询任何.NET数组或集合。一些运算符返回 IEnumerable 对象,而其他的一些运算符返回标量。返回变量的运算符立即执行,并返回一个值。
被查询的集合叫做序列,它必须实现 IEnumerable<T> 接口Stytem.Linq.Enumbrable 类声明了标准查询运算符方法,即标准查询运算符实质是扩展了 IEnumerable<T> 泛型类的 扩展方法

3.1.1 委托作为参数

因为运算符是 IEnumerable<T> 的扩展方法,所以每一个运算符的第一个参数是 IEnumerable<T> 对象的引用,之后的参数可以是任何类型。
而很多运算符接受泛型委托作为参数。这些运算符常常需要提供代码来指示运输费如何执行它的操作,LINQ定义了两套泛型委托类型与标准查询一起使用,即 Func 委托与 Action 委托,各有17个成员。

// Func
public delegate TR Func<out TR>(); // 没有参数方法
public delegate TR Func<in T1, out TR>(T1 a1); //接受一个参数方法

// Action, 没有返回值
public delegate void Action(); // 没有参数方法
public delegate void Action<in T1>(T1 a1);

其中:

  • Func 委托有返回值,而 Action 委托没有返回值。
  • Func 委托中返回值类型 TR 必须作为类型参数列表中的最后一个并且为协变,而其他的类型参数都是逆变。

举例:

class Program
{
    static bool IsOdd(int x) // 委托对象使用的方法
    {
        return x%2 == 1;
    }
    static void Main()
    {
        int[] intArray = new int[] {3,4,5,6,7};
        Func<int, bool> myDel = new Func<int, bool>(IsOdd); // 委托对象
        var countOdd = intArray.Count(myDel); // 使用委托
        Counsole.WriteLine("Count of odd numbers:{0}", countOdd);
    }
}

注意:不需要声明 Func 委托类型,因为LINQ已经预定义了。

3.1.2 使用Lambda表达式作为参数

可以使用更简洁和跟局部化的方法来给运算符提供代码,那就是使用Lambda表达式,eg:

calss Program
{
    static void Main()
    {
        int[] intArray = new int[] {3,4,5,6,7,9};
        var countOdd = intArray.Count(x => x%2 == 1);
        Console.WriteLine("Count of odd numbers:{0}", countOdd);
    }
}

4 LINQ to XML

LINQ to XML可以创建、查询和操作XML,感觉就像JavaScript操作DOM树,而最大的不同是,LINQ to XML在搜索一个XML树时,不需遍历它。
需要了解XML文档的重要事项:

  • XML文档必须有一个根元素包含所有其他元素
  • XML标签区分大小写
  • XML也有属性(如同HTML中的classstyle等)
  • XML文档中的空格是有效的,这与HTML把空格作为单个空格输出不同。

4.1 XML类

LINQ to XML可以用两种方式和XML配合使用,第一种方式是作为简化的XML操作API,第二种是使用LINQ查询工具。

4.1.1 LINQ to XML API

LINQ to API由很多表示XML树组件的类组成,最常使用到的XML类有以下几种:

  • XDocument(XML树),可作为其直接地子节点有:
    • XProcessingInstruction 节点:提供XML该如何被使用和翻译的额外数据
    • XDeclaration 节点:包含XML使用的版本号、字符编码等
    • XDocumentType 节点
    • XElement 节点,若在 XDocument 中有最高级别的 XElement 节点,即它就是XML树中其它元素的 根元素
  • XElement,可作为其嵌套节点的有:
    • XElement
    • XComment 节点:注释
    • XAttribute

4.1.1.1 创建XML树

以下是使用XML API构建一个XML树的例子:

using System;
using System.Xml.Linq; // 需要命名空间

class Program
{
    static void Main()
    {
        XDocment employess1 = new XElement( // 创建XML文档
            new XElement("Employees",  // 创建根元素,第一参数是名称,第二参数是内容
                new XElement("Name", "Bob Smith"), // 创建元素
                new XElement("Name", "Sally Jones") // 创建元素
            )
        );

        employees1.Save("EmloyessFile.xml"); // 保存到文件

        // 使用 XDocument.Load 方法将保存的文档加载到新变量中
        XDocument employess2 = XDocument.Load("EmployessFile.xml");

        Console.WriteLine(employees2); // 显示文档
    }
}

4.1.1.2 获取XML树的元素

使用 Name 可以返回元素的名称

查询XML的方法如下

  • Nodes:作用于 XdocumentXElement,返回类型 IEnumberable<object>,作用是:返回当前节点的所有子节点(不管什么类型
  • Elements:作用于 XdocumentXElement,返回类型 IEnumberable<XElement>,作用是:返回所有当前节点的 XElement 子节点,或所有具有某个名字的子节点
  • Element:作用于 XdocumentXElement,返回类型 XElement,作用是:返回当前节点的第一个 XElement 子节点,或具有某个名字的子节点
  • Descendants:作用于 XElement,返回类型 IEnumberable<XElement>,作用是: 返回所有 XElement 子代节点 或所有具有某个名字的 XElement 子代节点,不管它们处于当前节点下嵌套多少个层次
  • DescendantsAndSelf:作用于 XElement,返回类型 IEnumberable<XElement>,作用是: 与 Descendants 一样,但是包括当前节点
  • Ancestors:作用于 XElement,返回类型 IEnumberable<XElement>,作用是: 返回所有上级 XElement 节点或者所有具有某个名字的上级 XElement 节点
  • AncestorsAndSelf:作用于 XElement,返回类型 IEnumberable<XElement>,作用是: 和 Ancestors 一样,但是包括当前节点
  • Parent:作用于 XElement,返回类型 XElement,作用是:返回当前节点的父节点

Nodes 方法有些返回的节点可能是不同类型,比如XElementXComment 等,此时可以使用类型作为参数的方法 OfType(type) 来指定返回某个类型的节点,如只获取 XComment 节点:

IEnumerable<XComment> comments = xd.Nodes().OfType<XComment>();

4.1.1.2 增加节点以及操作XML树

添加节点XML的方法如下:

  • Add:作用于父节点,在当前节点的既有子节点增加新的子节点
  • AddFirst:作用于父节点,在当前节点的既有子节点增加新的子节点
  • AddBeforeSelf
  • AddAfterSelf
  • Remove:删除当前所选的节点及其内容
  • RemoveNodes:删除当前所选的 XElement 及其内容
  • SetElement:作用父节点,设置节点的内容
  • ReplaceContent:替换节点的内容

4.1.1.2 使用XML特性

特性提供有关 XElement 节点的额外信息,在创建XML树时,可以使用XAttribute 构造函数来增加特性,如:

XElement rt = new XElement("Root",
    new XAttribute("color", "red"), // 特性构造函数
    new XElement("first")
)

// 获取特性
XAttribute color = rt.Attribute("color");
// 删除特性
rt.Attribute("color").Remove();
// 添加特性
rt.SetAttributeValue("size", null);

4.1.2 使用LINQ to XML的LINQ查询

var xyz = from e in rt.Elements()
          where e.Name.ToString().length == 5
          select e;

猜你喜欢

转载自blog.csdn.net/qq_38801354/article/details/79661017