C#学习笔记(八)—–LINQ查询的基础知识(上)

由于都是手打的字,难免会出现错误,请大家指正,感激。比如下面出现的LINQ、LInq、LINq等都是指的一个东西:LINQ


LINQ查询

  • lINQ是Language Integrated Query的简称,他可以视为一组语言和框架的特性的集合,我们可以使用LINQ对本地对象和远程数据源进行结构化的类型安全的检查。LINQ在C# 3.0 /Framework 3.5时引入。
  • LINQ用于查询任何实现了IEnumerable<T>的集合类型,数组啊,list啊,远程的数据源啊等等,LINQ具有编译时类型检查和动态查询组合这两大优点。
  • LINQ中所有核心元素都是在System.Linq和System.Linq.Expressions中。

入门

  • LINQ数据源的基本组成部分是序列和元素,序列是指任何实现了IEnumerable<T>的对象,元素是指这个序列中的每一个成员。像下面这个names:
    string[] names = { "Tom", "Dick", "Harry" };names是序列也是内存中的一个对象集合,我们也可以称之为本地序列,其中的Tom、Dick等是元素。
  • 查询运算符用于转换序列,通常,一个查询运算符接收一个输入的序列,并经过转换为一个输出序列,在System.Linq中定义了约40中运算符,这些运算符都是静态的扩展方法,成为标准查询运算符。
  • 提示我们把对本地序列进行的查询叫做本地查询或LInqToObject查询。Linq还支持对那些从远程数据源动态获取的序列进行查询。这些序列需要实现IQueryble<T>接口,而在Queryble<T>类中则有一组相应的标准查询运算符进行支持,一个查询可以理解为使用一个查询运算符对一个序列进行转换的表达式。最简单的查询包含一个查询运算符和一个输入序列。例如我们使用where运算符:
string[] names = { "Tom", "Dick", "Harry" };
IEnumerable<string> filteredNames = System.Linq.Enumerable.Where
(names, n => n.Length >= 4);
foreach (string n in filteredNames)
Console.WriteLine (n);
输出:
Dick
Harry

因为查询运算符都是扩展方法,所以我们可以像调用实例方法那样在names上面调用where:

IEnumerable<string> filteredNames = names.Where (n => n.Length >= 4);

想要这些通过编译,你必须导入System.Linq命名空间,下面这个完整的例子:

using System;
usign System.Collections.Generic;
using System.Linq;
class LinqDemo
{
static void Main()
{
string[] names = { "Tom", "Dick", "Harry" };
IEnumerable<string> filteredNames = names.Where (n => n.Length >= 4);
foreach (string name in filteredNames) Console.WriteLine (name);
}
}
Dick
Harry

提示:实际上我们也可以通过var来定义查询的结果集进一步精简代码,但是这样会降低可读性,并且在vs以外的IDE中也不会得到这种智能提示。
大多数查询运算符都接受一个lambda表达式作为参数,lambda表达式用于对查询进行格式化。本例中的lambda表达式如下:n=>n.Length>=4这里的输入参数对应一个输入元素,办理中,输入参数n序列中的一个元素,可以看出是string类型的。Where查询运算符要求这个lambda表达式返回一个bool值,如果返回true,那么表示这个元素应该包含在返回的序列里。下面是Where运算符的签名:

public static IEnumerable<TSource> Where<TSource>
(this IEnumerable<TSource> source, Func<TSource,bool> predicate)

下面的查询要找出序列中所有包含字母a的单词的集合:

IEnumerable<string> filteredNames = names.Where (n => n.Contains ("a"));
foreach (string name in filteredNames)
Console.WriteLine (name); // Harry

到目前为止,我们使用了标准查询运算符和lambda表达式进行编写的Linq查询语句,这种查询方式是可以连续拼接起来使用的,我们称这种方法为运算符流语法。C#还提供了另外一种语法来编写linq语句 ,叫做查询表达式语法,使用这种查询表达式,可以将前面的语句编写成:

IEnumerable<string> filteredNames = from n in names
where n.Contains ("a")
select n;

这两种语法是互补的,在下面的文字中,我们会详细的介绍每一种用法。

运算符流语法

运算符流语法是最基本的查询语法,我们后续还会介绍连续使用多个查询运算符进行查询的语句,从中我们可以看到扩展方法的强大功能。此外,还会介绍lambda表达式的书写规则和几个新的查询运算符。

连续使用查询运算符

前面的介绍使用的都是一个查询运算符,这次使用多个:

using System;
using System.Collections.Generic;
using System.Linq;
class LinqDemo
{
static void Main()
{
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
IEnumerable<string> query = names
.Where (n => n.Contains ("a"))
.OrderBy (n => n.Length)
.Select (n => n.ToUpper());
foreach (string name in query) Console.WriteLine (name);
}
}
JAY
MARY
HARRY

这里先用Where将序列中带a的名字都拿出来,再把新的序列中的元素按照长度排序,最后将这些名字都转化成大写后再输出。
需要注意的是,上面的每个查询运算符中的n都是各自独立的,私有的。可以用下面的示例说明这三个n的关系:

void Test()
{
foreach (char c in "string1") Console.Write (c);
foreach (char c in "string2") Console.Write (c);
foreach (char c in "string3") Console.Write (c);
}

Where、OrderBy、Select都是标准查询运算符,对应System.Linq命名空间中的三个扩展方法。Where查询运算符的作用是筛选传进来的序列,OrderBy是都传进来的序列进行排序,而Select的作用是投影,将参数内的lambda表达式的参数n进行一个提取,甚至可以生成一个和之前序列毫无关系的对象,一句话总结:Where对序列进行的是纵向的缩小(把没用的行都扔了),Select对序列进行的是横向的缩小(把元素的一些没用的字段都扔了)。特别需要注意的是查询运算符只是生成了一个新的序列,并不会改变原有的序列,这种设计师符合函数式编程规范的,LINQ的思想实际上起源于函数式编程。
下面是这三种运算符多对应的扩展方法:

public static IEnumerable<TSource> Where<TSource>
(this IEnumerable<TSource> source, Func<TSource,bool> predicate)
public static IEnumerable<TSource> OrderBy<TSource,TKey>
(this IEnumerable<TSource> source, Func<TSource,TKey> keySelector)
public static IEnumerable<TResult> Select<TSource,TResult>
(this IEnumerable<TSource> source, Func<TSource,TResult> selector)

当我们以上面的实例的方式连续使用三个查询运算符进行查询时,可以将各个运算符的处理过程理解成一个传送带,从中可以更直观的看出每一个运算符的输入及输出:
这里写图片描述
当然我们也可以把他们分别弄成一个语句,而不是把他们放在一个语句中:

// You must import the System.Linq namespace for this to compile:
IEnumerable<string> filtered = names .Where (n => n.Contains ("a"));
IEnumerable<string> sorted = filtered.OrderBy (n => n.Length);
IEnumerable<string> finalQuery = sorted .Select (n => n.ToUpper());

这里的finalQuery得到的结果和上面的结果是一样的。实际上这三个语句每一个都会输出一个新的集合结果,例如:

foreach (string name in filtered)
Console.Write (name + "|"); // Harry|Mary|Jay|
Console.WriteLine();
foreach (string name in sorted)
Console.Write (name + "|"); // Jay|Mary|Harry|
Console.WriteLine();
foreach (string name in finalQuery)
Console.Write (name + "|"); // JAY|MARY|HARRY|

扩展方法的重要性

前面我们提到,每个查询运算符对应着一个扩展方法,前面的示例中我们都是使用查询运算符对集合进行操作,实际上,也可以直接使用这些扩展方法来完成查询,例如:

IEnumerable<string> filtered = Enumerable.Where (names,
n => n.Contains ("a"));
IEnumerable<string> sorted = Enumerable.OrderBy (filtered, n => n.Length);
IEnumerable<string> finalQuery = Enumerable.Select (sorted,
n => n.ToUpper());

实际上这也是编译器在遇到标准查询运算符的处理方式,把它们编译成对应函数进行调用,但是,这种调用方式是不可取的,举个例子来说:如果我们想在一个LINQ表达式中使用多个运算符,可以这样写:

IEnumerable<string> query = names.Where (n => n.Contains ("a"))
.OrderBy (n => n.Length)
.Select (n => n.ToUpper());

这种写法可以很清楚的表示出数据是从左边到右边一次处理的,但是如果直接调用Enumerable类中的方法来完成这个查询,表达式就不那么容易理解了:

IEnumerable<string> query =
Enumerable.Select (
Enumerable.OrderBy (
Enumerable.Where (
names, n => n.Contains ("a")
), n => n.Length
), n => n.ToUpper()
);

使用Lambda表达式

在前面的实例中,我们把如下Lambda表达式作为参数传给了Where运算符:

n => n.Contains ("a") // Input type=string, return type=bool.//返回一个bool值的表达式我们可以称之为断言。

对于不同的查询运算符来说,Lambda表达式的作用也不同。在Where运算符中,Lambda表达式用于判断一个元素是否应该被包含在输出序列中。也就是判断元素是否符合筛选条件;在OrderBy运算符中,Lambda表达式用于将集合中的每个元素映射到他们的排序键上;在Select运算符中,Lambda表达式用于定义输入序列以何种方式、格式进行输出。需要注意的是,查询运算符的Lambda表达式针对的是集合中的每个元素,而不是集合整体。
实际上,运算符会自动识别传递给他的Lambda表达式的意义,典型的情况是:Lambda表达式会作用于序列中的每个元素,并且在操作每个元素的时候都会都对Lambda表达式进行解析,这使得查询运算符看起来很神奇,但它的底层实现是很容易理解的,一Enumerable类中的Where方法为例:它的实现如下:

public static IEnumerable<TSource> Where<TSource>
(this IEnumerable<TSource> source, Func<TSource,bool> predicate)
{
foreach (TSource element in source)
if (predicate (element))
yield return element;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 1 LINQ查询结果集 1 2 System.Array 数组 1 2.1 基于System.Array定义数组 1 2.2 基于类型定义数组 1 2.3 数组元素的清空 1 2.4 System.Array类静态成员 1 2.5 不用循环填充数组 1 2.6 数组类实例成员 2 3 System.Collections 集合 2 3.1 ArrayList 2 3.1.1 实例成员 2 3.1.2 静态成员 2 3.2 List<T> 3 3.3 Hashtable 6 3.4 SortedList 6 3.5 SortedList<TKey,TValue> 7 3.6 Queue<T> 8 3.7 Stack<T> 8 3.8 LinkedList<T> 8 3.9 HashSet<T> 9 4 System.Linq 10 4.1 System.Linq.Enumerable 10 4.2 System.Linq.Queryable 10 4.3 System.Linq.Lookup <TKey,TElement> 10 4.4 System.Linq.Expressions.Expression 10 5 接口 10 5.1 IEnumerable 、IEnumerator 10 5.1.1 正常使用 10 5.1.2 C#的 yield 12 5.2 IEnumerable <T> 12 5.3 IEnumerator <T> 12 5.4 ICollection 12 5.5 ICollection <T> 13 5.6 IList 13 5.7 IList <T> 13 5.8 IEqualityComparer 13 5.9 IEqualityComparer <T> 13 5.10 IDictionary 13 5.11 IDictionary <TKey,TValue> 13 5.12 IDictionaryEnumerator 13 5.13 IComparer 13 5.13.1 接口方法说明 int Compare(object x, object y) 13 5.13.2 ArrayList.Sort (IComparer) 方法 13 5.14 IComparer <T> 14 5.14.1 接口方法override int Compare(T x, T y)说明 14 5.14.2 List.Sort (IComparer) 方法 14 5.15 System.Linq.IGrouping<T> 14 5.16 System.Linq.ILookup<TKey,TElement> 14 5.17 System.Linq.IOrderedEnumerable<T> 14 5.18 System.Linq.IOrderedQueryable 14 5.19 System.Linq.IOrderedQueryable<T> 15 5.20 System.Linq.IQueryable 15 5.21 System.Linq.IQueryable<T> 15 5.22 System.Linq.IQueryProvider 15 6 集合扩展方法 15 6.1 集合扩展方法的实现:一个Where的例子 15 6.2 延迟类 15 6.2.1 Select 选择 16 6.2.2 SelectMany 选择 16 6.2.3 Where 条件 16 6.2.4 OrderBy 排序升 17 6.2.5 OrderByDescending 排序降 17 6.2.6 GroupBy 分组 17 6.2.7 Join 联合查询 18 6.2.8 GroupJoin 18 6.2.9 Take 获取集合的前n个元素 19 6.2.10 Skip 跳过集合的前n个元素 19 6.2.11 Distinct 过滤集合中的相同项 19 6.2.12 Union 连接不同集合,自动过滤相同项 19 6.2.13 Concat 连接不同集合,不会自动过滤相同项 19 6.2.14 Intersect 获取不同集合的相同项(交集) 20 6.2.15 Except 从某集合中删除其与另一个集合中相同的项 20 6.2.16 Reverse 反转集合 20 6.2.17 TakeWhile 条件第一次不成立就跳出循环 20 6.2.18 SkipWhile 条件第一次不成立就失效,将后面的数据全取 20 6.2.19 Cast 将集合转换为强类型集合 21 6.2.20 OfType 过滤集合中的指定类型 21 6.3 不延迟(浅复本) 21 6.3.1 Single 集合中符合条件的唯一元素,浅复本 21 6.3.2 SingleOrDefault 集合中符合条件的唯一元素(没有则返回类型默认值),浅复本 21 6.3.3 First 集合的第一个元素,浅复本 21 6.3.4 FirstOrDefault 集合中的第一个元素(没有则返回类型默认值),浅复本 22 6.3.5 Last 集合中的最后一个元素,浅复本 22 6.3.6 LastOrDefault 集合中的最后一个元素(没有则返回类型默认值),浅复本 22 6.3.7 ElementAt 集合中指定索引的元素,浅复本 22 6.3.8 ElementAtOrDefault 集合中指定索引的元素(没有则返回类型默认值),浅复本 22 6.3.9 Contains 判断集合中是否包含有某一元素 22 6.3.10 Any 判断集合中是否有元素满足某一条件 22 6.3.11 All 判断集合中是否所有元素都满足某一条件 23 6.3.12 SequenceEqual 判断两个集合内容是否相同 23 6.3.13 Count 、LongCount集合中的元素个数 23 6.3.14 Average 、Sum集合平均值求和 23 6.3.15 Max、Min 集合最大值,最小值 24 6.3.16 Aggregate 根据输入的表达式获取一个聚合值 24 6.3.17 DefaultIfEmpty 查询结果为空则返回默认值,浅复本 24 6.3.18 ToArray 将集合转换为数组,浅复本 24 6.3.19 ToList 将集合转换为List<T>集合,浅复本 25 6.3.20 ToDictionary 将集合转换为<K, V>集合,浅复本 25 7 Lambda表达式 25 7.1 例1(比效) 25 7.2 例2(多参) 27 7.3 例3(list.Where) 27 7.4 Lambda表达式中Lifting 28 8 QuerySyntax 查询语法 29 8.1 from in select 30 8.2 orderby 排序 30 8.3 group by into 分组 31 8.4 join in on equals 联合查询 33 8.5 into 汇总 33 9 DataSource 数据绑定 34

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值