C#高级特性之LINQ(语言集成查询)

什么是linq, 它解决了什么问题

在工程中我们少不了要定义类或者结构去储存数据,这些数据将被临时地储存在内存中,现在我们想要对其完成一些类似于查找、过滤等等常见的任务的时候,我们该如何去做呢? 以前我们可以自己写代码去对集合中的每个对象进行遍历,检查变量的每个字段看其是否满足条件。

LINQ(Language Integrated Query)即语言集成查询, linq可以像SQL一样用来从对象集合/SQL数据库/XML中查询想要的数据.

自定义类需要实现了IEnumerable或者IEnumerable接口,才可以进行LINQ查询。

LINQ 主要包含以下三部分

  • LINQ to Objects 主要负责对象的查询
  • LINQ to XML 主要负责 XML 的查询
  • LINQ to ADO.NET(Linq to SQL(少用)/Linq to DataSet/Linq to Entities(重点)) 主要负责数据库的查询,

LINQ 主要解决了什么问题

  • 面向对象数据访问两个领域长期分裂,各自为政;
  • 编程语言中的数据类型与数据库中的数据类型形成两套体系
    如:C#中的字符串string 在SQL中使用NVarchar/ Varchar/ Char来表示;
  • SQL和XML都有各自的查询语言,而对象没有自己的查询语言
    例如要从List<>集合或者数组中找到符合要求的元素,非常困难;

linq查询方式

  • Linq查询方法, 链式查询方式list.aaa().bbb().ccc().Select()
  • Linq查询语句, 类似SQL的查询语句from num in listData where num > 100 select num;

大部分的查询语句的关键词都对应查询方法, 例如where语句对应where()方法, select语句对应select()方法等, 当然很多聚合查询(count,max)方法都还没有对应查询语句, 当然可以先使用查询语句查询出列表后, 再使用聚合方法进行统计也行.

// 查询方法
listData.where(c=>c.num > 100).Count()
// 混合查询 = (查询语句+查询方法)
(from num in listData where num > 100 select num).Count()

LINQ查询方法详解

标准查询操作符 说明
where OfType 筛选操作符定义了返回元素的条件。在Where查询操作符中,可以使用谓词,例如Lambda表达式定义的谓词,来返回布尔值。OfType根据类型筛选元素,只返回TResult的类型元素
Select 和SelectMany 投射操作符用于把对象转换为另一个类型的新对象。Select和SelectMany定义了根据选择器函数选择结果值的投射。
OrderByThenByOrderByDescendingThenByDescending 、Reverse 排序操作符改变所返回的元素的顺序。OrderBy按升序排列,OrderByDescending按降序排列。如果第一次排序结果很类似,就可以使用ThenBy和ThenByDescending操作符进行第二次排序。Reverse反转集合中的元素顺序。
GroupBy、ToLookUp 组合运算符把数据放在组里面。GroupBy操作符组合有公共键的元素。ToLookUp通过创建一个一对多的字典,来组合元素。
Join、GroupJoin 链接运算符用于合并不直接相关的集合。使用Join操作符,可以根绝键选择器函数连接两个集合,这类似于SQL中的Join。GroupJoin操作符连接两个集合,组合其结果。
Any、All、Contains 如果元素序列满足指定的条件,两次操作符就返回布尔值。Any、ALll和Contains都是限定符操作符。Any确定集合中是否有确定满足谓词函数的元素。ALll确定集合中的所有元素是否都满足谓词函数。Contains检查某个元素是否在集合中。这些操作符都返回一个布尔值。
TakeSkip、TakeWhile、SkipWhile 分区操作符返回集合的一个子集,Take、Skip、TakeWhile、SkipWhile都是分区操作符。使用它们可以得到部分结果,使用Take必须指定要从集合中提取的元素个数;Skip跳过指定个数的元素,提取其它元素;TakeWhile提取条件为真的元素。
Distinct、Union、Intersect、Except、Zip Set操作符返回一个集合。Distinct从集合中删除重复的元素,除了Distinct之外,其它的Set操作符都需要两个集合。Union返回出现在其中一个集合中的唯一元素。Intersect返回两个集合中都有的元素。Except返回值出现在一个集合中的元素。Zip是.NET 4新增的,它把两个集合合并为一个。
First、FirstOrDefault、Last、LastOrDefault、ElementAt、ElementAtOrDefault、Single、SingleOrDefault 这些元素操作符仅返回一个元素。First返回第一个满足条件的元素。FirstOrDefault类似于First,单如果没有找到满足条件的元素,就返回类型的默认值。Last返回最后一个满足条件的元素。ElementAt指定了要返回的元素的位置。Single只返回一个满足条件的元素。如果有多个元素都满足条件,就抛出一个异常。
CountSumMinMaxAverage、Aggregate 聚合操作符计算集合的一个值。利用这些聚合操作符,可以计算所有值的总和、所有元素的个数、值最大和最小的元素,以及平均值等等。
ToArray、ToEnumerable、ToList、ToDictionary、Cast 这些转换操作符将集合转换为数组:IEnumerable、IList、IDictionary等。
Empty、Range、Repeat 这些生成操作符返回一个心机和。使用Empty时集合是空的;Range返回一系列数字;Repeat返回一个始终重复一个值的集合。

LINQ查询语句详解

LINQ查询表达式以from子句开始,以select/group子句结束。在这两个子句之间可以跟零个或者多个from、let、where、join或者orderby子句。

子句 说明
from 指定数据源和范围变量(类似于迭代变量)。
where 根据一个或多个由逻辑“与”和逻辑“或”运算符(&& 或 ||)分隔的布尔表达式筛选源元素。
select 指定当执行查询时返回的序列中的元素将具有的类型和形式。
group 按照指定的键值对查询结果进行分组。
into 提供一个标识符,它可以充当对 join、group 或 select 子句的结果的引用。
orderby 基于元素类型的默认比较器按升序或降序对查询结果进行排序。
join 基于两个指定匹配条件之间的相等比较来联接两个数据源。
let 引入一个用于存储查询表达式中的子表达式结果的范围变量。
in join 子句中的上下文关键字。
on join 子句中的上下文关键字。
equals join 子句中的上下文关键字。
by group 子句中的上下文关键字。
ascending orderby 子句中的上下文关键字。
descending orderby 子句中的上下文关键字。

关于linq的效率问题

linq与普通循环查询的效率对比, 简单查询linq快两倍.

/// <summary>
/// 效率测试
/// </summary>
/// <param name="testCount">第几次测试</param>
private static void timeTest(int testCount)
{
    const int listCount = 10000000;         // 数组长度
    Random random = new Random();           // 数据随机构建值

    // 数组构建 
    List<int> listData = new List<int>();
    for (int i = 0; i < listCount; i++)
    {
        listData.Add(random.Next(10000));
    }

    // LINQ 测试
    Stopwatch linq_Stopwatch = new Stopwatch();
    linq_Stopwatch.Start();
    var linqList = from num in listData
                    where num > 100
                    select num;
    var linqCount = linqList.Count();   
    linq_Stopwatch.Stop();

    // 普通方式 测试
    Stopwatch before_Stopwatch = new Stopwatch();
    before_Stopwatch.Start();
    List<int> beforeList = new List<int>(listCount);
    for (int i = 0; i < listData.Count(); i++)
    {
        if (listData[i] > 100)
            beforeList.Add(listData[i]);
    }
    var beforeCount = beforeList.Count;
    before_Stopwatch.Stop();

    // 打印结果
    Console.WriteLine(String.Format("第{0}次测试,测试:{5}条数据。\n\r \t LINQ用时:{1}毫秒,筛选了{2}条数据。\n\r\t 普通用时:{3}毫秒,筛选了{4}条数据。\r\n",
        testCount, linq_Stopwatch.ElapsedMilliseconds, linqCount, before_Stopwatch.ElapsedMilliseconds, beforeCount, listCount));
}

93tb4q9hmd.jpeg

扩展阅读

注意:linq为c#语言特性,java是没有这个特性的, 在java8中新增了java OQL同sql语言

LINQ查询表达式详解(1)
LINQ查询表达式详解(2)
C#进阶之LINQ(1)必备基础知识
C#进阶之LINQ(2)Linq查询语句与查询方法
C# LINQ 详解 From Where Select Group Into OrderBy Let Join
JVM 对象查询语言(OQL)

此处评论已关闭