添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

CSDN博客链接:
https://blog.csdn.net/weixin_44293426/article/details/136058129?spm=1001.2014.3001.5501

语言集成查询(英语:Language Integrated Query,缩写:LINQ),
发音"link",是微软的一项技术,新增一种自然查询的SQL语法到.NET Framework的编程语言中,
当前可支持C#以及Visual Basic .NET语言。
2007年11月19日随.NET Framework 3.5发布了LINQ技术。
包括LINQ to Objects、LINQ to SQL、LINQ to Datasets、LINQ to Entities、LINQ to Data Source、LINQ to XML/XSD等

  1. 查询表达式是一种使用查询语法表示的表达式,它用于查询和转换来自任意支持LINQ的数据源中的数据。

  2. 查询表达式使用常见的C#语言构造,易读简洁,容易掌握。

  3. 它由一组类似于SQL或XQuery的声明性语法编写的子句组成。

  4. 每一个子句可以包含一个或多个C#表达式,这些C#表达式本身也可能是查询表达式或包含查询表达式。

    查询表达式必须以 from 子句开头,以 select group 子句结束。在第一个 from 子句和最后一个 select 子句或 group 子句之间,可以包含一个或多个 where 子句、 let 子句、 join 子句、 orderby 子句和 group 子句,甚至还可以是 from 子句。

from 子句指定的数据源类型必须为 IEnumerable IEnumerable<> 或一种派生类型

它包括8个基本子句,具体说明如下所示。

  1. from子句
    指定查询操作的数据源和范围变量。

  2. select子句
    指定查询结果的类型和表现形式。

  3. where子句
    指定筛选元素的逻辑条件。

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp2
internal class Program
static void Main(string[] args)
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        Console.WriteLine("\t-- Linq --");
        var res = from m in list
                  where m % 2 == 0
                  select m;
        foreach (var r in res)
            Console.WriteLine(r);
        Console.WriteLine("\t-- Lambda --");
        var res1 = list.FindAll(m => m % 2 == 0);
        foreach (var r in res1)
            Console.WriteLine(r);
        Console.ReadKey();
  1. let子句
    引入用来临时保存查询表达式中的子表达式结果的范围变量。

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp2
internal class Program
static void Main(string[] args)
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        Console.WriteLine("\t-- Linq --");
        var res = from m in list
                  let isFlag = m % 2 == 0 // from m in list
                  where isFlag            // where m % 2 == 0
                  select m;               // select m;
        foreach (var r in res)
            Console.WriteLine(r);
        Console.WriteLine("\t-- Lambda --");
        var res1 = list.FindAll(m => m % 2 == 0);
        foreach (var r in res1)
            Console.WriteLine(r);
        Console.ReadKey();

输出结果同上

  1. orderby子句
    对查询结果进行排序操作,包括升序和降序。

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp2
internal class Program
static void Main(string[] args)
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        Console.WriteLine("\t-- Linq --");
        //var res = from m in list
        //          where 1 < m && m < 6
        //          orderby m descending // 降序
        //          select m;
        var res = from m in list
                  where 1 < m && m < 6
                  orderby m ascending // 升序
                  select m;
        foreach (var r in res)
            Console.WriteLine(r);
        Console.WriteLine("\t-- Lambda --");
        //var res1 = list.Where(m => 1 < m && m < 6).OrderByDescending(m => m); // 降序
        var res1 = list.Where(m => 1 < m && m < 6).OrderBy(m => m); // 升序
        foreach (var r in res1)
            Console.WriteLine(r);
        Console.ReadKey();

ascending可以省略不写,因为默认是升序排列

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp2
internal class Program
static void Main(string[] args)
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        Console.WriteLine("\t-- Linq --");
        var res = from m in list
                  where 1 < m && m < 6
                  orderby m % 2, m descending // 先按照 m % 2 升序排序, 再按照 m 降序排序
                  select m;
        foreach (var r in res)
            Console.WriteLine(r);
        Console.WriteLine("\t-- Lambda --");
        var res1 = list.Where(m => 1 < m && m < 6)
                       .OrderBy(m => m % 2)
                       .ThenByDescending(m => m);
        foreach (var r in res1)
            Console.WriteLine(r);
        Console.ReadKey();
  1. group子句
    对查询结果进行分组。

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp2
internal class Program
static void Main(string[] args)
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        Console.WriteLine("\t-- Linq --");
        var res = from m in list
                  where 1 < m && m < 6
                  group m by m % 2;
        foreach (var r in res)
            Console.WriteLine("Key: " + r.Key);
            foreach (var s in r)
                Console.Write("Value: " + s + "\t");
            Console.WriteLine();
        Console.WriteLine("\t-- Lambda --");
        var res1 = list.Where(m => 1 < m && m < 6)
                       .GroupBy(m => m % 2);
        foreach (var r in res1)
            Console.WriteLine("Type: " + r);
        foreach (var r in res1)
            Console.WriteLine("Key: " + r.Key);
            foreach (var s in r)
                Console.Write("Value: " + s + "\t");
            Console.WriteLine();
        Console.ReadKey();
  1. into子句
    提供一个临时标识符。join子句、group子句或select子句可以通过该标识符引用查询操作中的中间结果。

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp2
internal class Program
static void Main(string[] args)
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        Console.WriteLine("\t-- Linq --");
        var res = from m in list
                  where 1 < m && m < 6
                  group m by m % 2 into tmp
                  from t in tmp
                  where t > 3
                  select t;
        foreach (var r in res)
            Console.WriteLine(r);
        Console.WriteLine("\t-- Lambda --");
        var res1 = list.Where(m => 1 < m && m < 6)
                       .GroupBy(m => m % 2)
                       .SelectMany(m => m)
                       .Where(m => m > 3);
        foreach (var r in res1)
            Console.WriteLine(r);
        Console.ReadKey();
  1. join子句
    连接多个用于查询操作的数据源。

8.1 内部联接(inner join – join on)
元素的联接关系必须同时满足两个数据源,类似于SQL语句中的inner join子句

join子句的内部联接要求两个数据源必须存在相同的值,即两个数据源都必须存在满足联接关系的元素

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp2
internal class Program
static void Main(string[] args)
List<int> list1 = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> list2 = new List<int>() { 0, 2, 4, 6, 8 };

        Console.WriteLine("\t-- Linq --");
        var res1 = from m in list1
                  where 1 < m && m < 6
                  join n in list2 on m equals n
                  select m;
        foreach (var r in res1)
            Console.WriteLine(r);
        Console.WriteLine("\t-- Lambda --");
        var res2 = list1.Where(m => 1 < m && m < 6)
                        .Join(list2, m => m, n => n, (m, n) => m);
        foreach (var r in res2)
            Console.WriteLine(r);
        Console.ReadKey();

8.2 分组联接(group join – join on into)
包含into子句的join子句

将左数据源与右数据源的元素依次匹配。

左数据源的所有元素都出现在查询结果中。

若在右数据源中找到匹配项,则使用匹配的数据,否则用空表示

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp2
internal class Program
static void Main(string[] args)
List<int> list1 = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> list2 = new List<int>() { 0, 2, 4, 6, 8 };

        Console.WriteLine("\t-- Linq --");
        var res1 = from m in list1
                  where 1 < m && m < 6
                  join n in list2 on m equals n into g
                  select new
                      ID = m,
                      Values = g
        foreach (var r in res1)
            Console.WriteLine("type: " + r);
            Console.WriteLine("Key: " + r.ID);
            foreach (var s in r.Values)
                Console.Write("Value: " + s + "\t");
            Console.WriteLine();
        Console.WriteLine("\t-- Lambda --");
        var res2 = list1.Where(m => 1 < m && m < 6)
                        .GroupJoin(list2, m => m, n => n, (m, g) => new { ID = m, Values = g });
        foreach (var r in res2)
            Console.WriteLine("Key: " + r.ID);
            foreach (var s in r.Values)
                Console.Write("Value: " + s + "\t");
            Console.WriteLine();
        Console.ReadKey();

8.3 左外部联接(left outer join – join on into default)
元素的联接关系必须满足联接中的左数据源,类似于SQL语句中的left join子句

join子句的左外部联接将返回左侧数据源序列中的所有元素,就算他们在右侧序列中没有匹配的元素也是这样

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp2
internal class Program
static void Main(string[] args)
List<int> list1 = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> list2 = new List<int>() { 0, 2, 4, 6, 8 };

        Console.WriteLine("\t-- Linq --");
        var res1 = from m in list1
                   where 1 < m && m < 6
                   join n in list2 on m equals n into g
                   // 如果序列为空, 则DefaultIfEmpty()方法返回只包含一个元素的序列, 该元素类型的值为默认值(在此为0)
                   from ab in g.DefaultIfEmpty()
                   select ab;
        foreach (var r in res1)
            Console.Write(r + "\t");
        Console.WriteLine("\n\t-- Lambda --");
        var res2 = list1.Where(m => 1 < m && m < 6)
                        .GroupJoin(list2, m => m, n => n, (m, g) => g.DefaultIfEmpty());
        foreach (var r in res2)
            Console.WriteLine("type: " + r);
            foreach (var s in r)
                Console.Write(s + "\t");
            Console.WriteLine();
        Console.ReadKey();

不难发现,这三种联接要求的严格程度是越来越低的

  1. 内部联接最高,只获取满足条件的

  2. 分组联接次之,不满足条件返回空

  3. 左外部联接最宽容,不满足条件给默认值

  4. Linq、Lambda表达式使用时机
    在项目中使用 LINQ 或 Lambda 表达式的选择通常取决于项目的特定需求、团队的编码风格以及性能考虑等因素。

    虽然 LINQ 和 Lambda 表达式都是 C# 中强大的功能,但它们各自有着不同的优缺点。

LINQ 语法:

可读性高:LINQ 语法更接近自然语言,更容易理解,特别是对于非专业程序员或者初学者来说。
可组合性强:LINQ 提供了丰富的查询操作符,可以方便地进行多个操作的组合,编写出简洁、清晰的查询语句。
与数据库查询类似:LINQ 语法与 SQL 查询语句相似,对于熟悉数据库的开发者来说更容易上手。
Lambda 表达式:

更灵活:Lambda 表达式可以实现更复杂的逻辑,可以直接嵌入到代码中,提高了代码的灵活性和可读性。
更适合链式调用:Lambda 表达式更适合于链式调用的场景,比如使用 Where、Select、OrderBy 等方法进行数据筛选和排序。
建议和注意事项:

团队编码风格:在团队中,建议统一选择一种风格,以保持代码的一致性和可维护性。
性能考虑:在性能要求高的场景下,Lambda 表达式通常比 LINQ 语法更高效,因为 Lambda 表达式通常可以通过委托直接调用方法,而 LINQ 语法可能会生成更多的中间代码。
项目需求:根据项目的需求和特点选择合适的编码方式。如果项目需要更直观的查询语法、更易于理解的代码,可以选择 LINQ 语法;如果需要更灵活、更高效的操作方式,可以选择 Lambda 表达式。
综上所述,虽然 LINQ 语法在可读性方面更优,但 Lambda 表达式更灵活、更适合链式调用,因此在项目中的选择取决于团队的实际需求和偏好。

在Enumerable类中还有很多方法支持Lambda写法,学无止境,加油!

using System;
using System.Linq;

namespace ConsoleApp2
internal class Program
static void Main(string[] args)
int[] arr = new int[] { 1, 2, 4, 5, 7, 8 };
// 创建一个查询表达式i, 查询arr数组中大于3的元素和10的乘积
var i = from e in arr where e > 3 select e * 10;
//var i = arr.Where(e => e > 3).Select(e => e * 10);
Console.WriteLine(“查询结果如下:”);
foreach (var a in i)
Console.Write(a + “\t”); // 打印输出结果
Console.WriteLine();
Console.ReadKey();