LINQ是什么
LINQ( Language Integrated Query )即语言集成查询
LINQ 是一组语言特性和API,使得你可以使用统一的方式编写各种查询。查询的对象包括XML、对象集合、SQL Server 数据库等等。
LINQ 主要包含以下三部分:
LINQ to Objects 主要负责对象的查询
LINQ to XML 主要负责 XML 的查询
LINQ to ADO.NET 主要负责数据库的查询
LINQ to SQL
LINQ to DataSet
LINQ to Entities
在没有LINQ以前,我们这样查询:
int[] numbers = new int[] { 6, 4, 3, 2, 9, 1, 7, 8, 5 };
List<int> even = new List<int>();
foreach (int number in numbers)
{
if (number % 2 == 0)
{
even.Add(number);
}
}
even.Sort();
even.Reverse();
我们有了LINQ! 我们这样查询:
int[] numbers = new int[] { 6, 4, 3, 2, 9, 1, 7, 8, 5 };
var even = numbers
.Where(p => p % 2 == 0)
.Select(p => p)
.OrderByDescending(p => p);
注:p => p 为Lambda表达式
Lambda表达式的简写方式
(参数列表) => {方法体}
参数列表 => {方法体}
如果方法体只包含一条语句时
(参数列表) => {方法体}
(参数列表) => 表达式
例:
(int x) => {return x+1;}
(int x) => x+1
多参数,推断类型参数列表,表达式方法体
(x, y) => x * y
无参数,表达式方法体
() => Console.WriteLine()
多参数,推断类型参数列表,多语句方法体
(x, y) => {
Console.WriteLine( x );
Console.WriteLine( y );
}
总体上说,匿名方法可以看作是Lambda 表达式的功能子集,但是两者存在以下区别:
Lambda 表达式的参数允许不指明参数类型,而匿名方法的参数必须明确指明参数类型
Lambda 表达式的方法体允许由单一表达式或者多条语句组成,而匿名方法不允许单一表达式形式
LinQ相关扩展方法:
1. Select()
原形:
public static IEnumerable<TResult> Select<TSource, TResult> (
this IEnumerable<TSource> source,
Func<TSource, TResult> selector )
说明
Select 方法本身是一个泛型扩展方法
它作用于IEnumerable<TSource>类型
它只接受一个 Func<TSource, TResult> 类型参数
Func<TSource, TResult> 是一个泛型委托,位于System名字空间下,System.Core.dll中
在这里 selector 是一个提取器
例:(foxRiver8是已定义的List<T>范型集合)
var q1 = foxRiver8.Select
(name => name.ToLower());
foreach (var item in q1)
{
Console.WriteLine(item);
}
获得所有元素的name属性值的小写形式
2. Where()
原形:
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate )
说明
Where方法也是一个泛型扩展方法
它和 Select() 一样作用于IEnumerable<TSource>类型
它只接受一个 Func<TSource, bool> 泛型委托参数
在这里 predicate 是一个判断条件
var q2 = foxRiver8
.Where( name => name.StartsWith("T"))
.Select(name => name.ToLower());
foreach (var item in q2)
{
Console.WriteLine(item);
}
name => name.StartsWith("T")以 Lambda 表达式形式出现的判断条件,注意返回值要求为 bool 类型
3. OrderBy()
原形:
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>( this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector )
说明
OrderBy方法也是一个泛型扩展方法
它和 Select() 一样作用于IEnumerable<TSource>类型
它只接受一个 Func<TSource, TKey > 类型参数
在这里 keySelector 指定要排序的字段
如果想降序排列可以使用OrderByDescending方法
例
var q3 = foxRiver8
.Where(name => name.Length >5)
.Select(name => name.ToLower())
.OrderBy(name => name.Substring(1,1))
foreach (var item in q3)
{
Console.WriteLine(item);
}
排序字段,这里指定按照姓名的第二个字母升序排列
4. GroupBy()
原形:
public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector )
说明
GroupBy方法和OrderBy方法非常类似,它也是一个泛型扩展方法
它和 OrderBy() 一样作用于IEnumerable<TSource>类型
它只接受一个 Func<TSource, TKey > 类型参数
在这里 keySelector 指定要分组的字段
var q4 = foxRiver8
.Where(name => name.Length > 5)
.Select(name => name.ToLower())
.GroupBy(name => name.Substring(0, 1));
foreach (var group in q4)
{
Console.WriteLine(group.Key);
Console.WriteLine("--------------------");
foreach (var item in group)
{
Console.WriteLine(item);
}
}
外层循环得到分组
内层循环得到分组中的项
Linq查询执行的时机
查询分为以下三步:获取数据源、定义查询、执行查询;
定义查询后,查询直到需要枚举结果时才被真正执行,这种方式称为“延迟执行(deferred execution)”;
当查询方法返回单一值时,查询立即执行;
因此,可以通过以下技巧在定义查询时就强制执行查询;
var even = numbers
.Where(p => p % 2 == 0)
.Select(p =>
{
Console.WriteLine("Hi! " + p.ToString());
return p;
}).Count();
LINQ查询的两种方式
事实上,LINQ查询存在以下两种形式
Method Syntax, 查询方法方式
主要利用 System.Linq.Enumerable 类中定义的扩展方法和 Lambda 表达式方式进行查询
上一章的例子都是以这种方式查询
Query Syntax, 查询语句方式
一种更接近 SQL 语法的查询方式
可读性更好
查询语句
int[] numbers = new int[] { 6, 4, 3, 2, 9, 1, 7, 8, 5 };
var even = from number in numbers
where number % 2 == 0
orderby number descending
select number;
查询方法
int[] numbers = new int[] { 6, 4, 3, 2, 9, 1, 7, 8, 5 };
var even = numbers
.Where(p => p % 2 == 0)
.OrderByDescending(p => p)
.Select(p => p);
List<Person> foxRiver8 = GetFoxRiver8();
var q = from p in foxRiver8
where p.Age <= 30 && p.FirstName.Length == 7
orderby p.Age descending
select new{
Name = p.FirstName + " " + p.LasName,
Age = p.Age};
foreach (var item in q)
{
Console.WriteLine(item.Name + " " + item.Age);
}
查询语句vs查询方法
查询语句与查询方法存在着紧密的关系
CLR本身并不理解查询语句,它只理解查询方法
编译器负责在编译时将查询语句翻译为查询方法
大部分查询方法都有对应的查询语句形式:如 Select() 对应 select 、 OrderBy() 对应 orderby
部分查询方法目前在C#中还没有对应的查询语句:如 Count()和Max() 这时只能采用以下替代方案
查询方法
查询语句 + 查询方法的混合方式;
一般情况下,建议使用可读性更好的查询语句
高级查询方法
聚合类
Count,Max/Min,Average
排序类
ThenBy
分区类
Take,TakeWhile,Skip,SkipWhile
集合类
Distinct去掉集合中的重复项
生成类
Range, Repeat
var q = foxRiver8
.OrderBy(p => p.FirstName)
.ThenBy(p => p.LasName)
.ThenBy(p => p.Age);
注意:ThenBy 对应的查询语句形式比较特别 并没有一个 thenby 子句 而是从属于 orderby 子句
TakeWhile 根据指定条件提取项
SkipWhile 根据指定条件跳过项
这里需要注意,TakeWhile ,SkipWhile 在执行时遇到不满足的条件是则停止执行
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var q = numbers.SkipWhile(i => i % 3 != 0)
.TakeWhile(i => i % 2 != 0);
foreach (var item in q)
{
Console.WriteLine(item);
}
输出结果为3。skipWhile先跳过1,2 我们得到3, 4, 5, 6, 7, 8, 9 TaKeWhile 提取项是取到3 当到4时不满足条件不会被输出同时停止执行
使用生成类查询方法时,需要注意以下几点:
和其他几类方法不同,Range/Repeat 不是扩展方法,而是普通的静态方法
Range 只能产生整数序列
Repeat 可以产生泛型序列
所有的查询方法都存放在 System.Linq.Enumerable 静态类中