【Linq】Linq语法速览及对应Lambda表达式写法

本文介绍了微软的LINQ技术,包括其在.NETFramework中的应用,如查询表达式的结构、C#语法示例,以及Lambda表达式的优点和适用场景。重点讲解了from到select子句的使用,以及各种子句的功能和Lambda表达式的灵活性。
摘要由CSDN通过智能技术生成

        语言集成查询(英语: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();
        }
    }
}

输出:

4. 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();
        }
    }
}

输出结果同上

5. 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();
        }
    }
}

6. 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();
        }
    }
}

7. 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();
        }
    }
}

8. 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. 左外部联接最宽容,不满足条件给默认值

9. Linq、Lambda表达式使用时机

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

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

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

Lambda 表达式:

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

建议和注意事项:

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

综上所述,虽然 LINQ 语法在可读性方面更优,但 Lambda 表达式更灵活、更适合链式调用,因此在项目中的选择取决于团队的实际需求和偏好。

10. 附

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

11. 填空练习

参考:

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();
        }
    }
}

  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值