Linq基础简介

导读:本文旨在以最简单的方式描述Linq的最基本和常用的使用方法,如果你希望对相关知识有更深层次的了解,可以在网上查阅其它相关资料。

什么是Linq?

  细心的人应该已经发现,在VS.Net 2008(或以上本版)新建一个WebForm(*.aspx)或其他类文件后,已经在该.cs文件的using部分已经添加了对Linq命名空间的引用:

 C# Code
1
using System.Linq;

  现在我们在这个.cs文件里的一个方法中,定义一个集合对象,然后通过VS.Net的智能提示浏览它包含的方法:


  标示了“下箭头”的方法就是System.Linq命名空间下定义的扩展方法。如果移除了“using System.Linq;”这一行,也就看不到这些方法了:


 根据我自己的理解,Linq就是定义在System.Linq命名空间下的一组针对集合对象的扩展方法。

  我所说的“集合对象”是指继承了System.Collections.IEnumerable接口的类对象,最常见集合对象的包括Array(string[]和int[]等)和List等,当然还包括这里的主角System.Linq.Queryable。但本文并不讨论Queryable,因为它涉及Linq深一层次的工作原理,不在本文的讨论范围内,如果你对此感兴趣,可以查询网上相关资料。

几个最常用的Linq方法

Where

  与Sql中的where类似,Linq中的Where返回集合中符合条件的记录。

 C# Code 
1
2
3
4
int[] numbers = { 0123456789 };
var smallNumbers = numbers.Where(n => n < 
5);
foreach (int number in smallNumbers)
    System.Console.WriteLine(
"小于5的数字:" + number);

  以上代码筛选出集合里面小于5的元素。

关于以上代码的解释:

  在C#里,var关键字表示隐式声明类型,类型由“=”右面的方法返回的类型决定。跟java script不同,C#里的var关键字声明的变量实际上是有明确的类型的,只是在代码中省略不标明,并且在声明后不能更改他的类型。在可以不使用var的情况下,我建议还是写明变量的类型,以增强代码的可读性。例如上“var smallNumbers = numbers.Where(n => n < 5)”等同于“System.Collections.Generic.IEnumerable<int> smallNumbers = numbers.Where(n => n < 5)”

  “n => n < 5”是一个Lambda表达式,Lambda表达式在C#里面是匿名委托/delegate(初学者可以暂时理解为匿名方法)的简写形式,n是方法的参数,“n <
5”是返回值(“<”返回bool),实际上“n => n < 5”也可以用一个委托代替。如果你想了解更多,可以查阅网上关于C# Lambda表达式的相关资料。

Any

  Any判断集合内是否存在符合条件的元素。

 C# Code 
1
2
3
4
int[] numbers = { 0123456789 };
if (numbers.Any(n => n < 5))
{
}
OrderBy和OrderByDescending

  OrderBy和OrderByDescending这两个方法的功能和Sql中的order by x和order by x desc类似。如果根据多个字段排序,后面再接上ThenBy或ThenByDescending。

 C# Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class FullName
{
    
/// <summary>
    /// 姓
    /// </summary>
    public string FirstName;
 
    
/// <summary>
    /// 名
    /// </summary>
    public string LastName;
 
    
public FullName(string firstName, string lastName)
    {
        
this.FirstName = firstName;
        
this.LastName = lastName;
    }
}
 
public void OrderTest()
{
    FullName[] names = { 
new FullName("张""三"), new FullName("李""四") };
    var orderedNames = names.OrderBy(n => n.FirstName).ThenByDescending(n => n.LastName);
}
Contains

  Contains方法在查询数据库时,功能和Sql中的like语句类似,查询字段是否包含指定的字符串。

 C# Code 
1
2
string[] names = { "Terry""Andy""King" };
var yNames = names.Contains(
"y");

  另外,Contains方法在查询数据库时,也有和Sql中的in语句类似的功能。

 C# Code 
1
2
3
FullName[] fullNames = { new FullName("张""三"), new FullName("李""四") };
string[] firstNames = { "张""黄""何" };
var names = fullNames.Where(n => firstNames.Contains(n.FirstName));
Select

  Select返回该参数内返回的对象集合:

 C# Code 
1
2
FullName[] names = { new FullName("张""三"), new FullName("李""四") };
var orderedNames = names.Select(n => 
new { n.FirstName, n.LastName });

  以上代码在姓名的集合中,取出FirstName和LastName两个属性组成一个新的集合。

关于以上代码的解释:

  “new{}”在C#中的作用是定义一个匿名对象,匿名对象使用前是不需要声明的,所以声明匿名对象的变量,只通过var关键字。如果我们想在匿名对象中使用别名,可以使用“=”操作符,例如“new { n.FirstName, n.LastName }”可以写成“new { FirstName =n.FirstName, LastName =n.LastName }”。针对这段代码,如果我们想把整个FullName对象取出来,可以把“n => new { n.FirstName, n.LastName }”写成“n => n”

Count

  与Sql中的count类似,Linq中的Count返回集合中的元素个数:

 C# Code 
1
2
int[] numbers = { 0123456789 };
int totalCount = numbers.Count();

Count方法也可以带条件参数:

 C# Code 
1
2
int[] numbers = { 0123456789 };
int smallNumberCount = numbers.Count(n => n < 5);

  在Linq中,Count方法常用于分页,告诉分页控件一共有多少条记录。

Skip和Take

  Skip方法的功能是跳过集合里指定数量个数的元素,取往后的元素。

  Take方法的功能是取集合里指定个数的元素。

  这两个方法在Linq里的最主要作用是用作分页:

 C# Code 
1
2
3
4
int[] numbers = { 0123456789 };
int pageSize = 3;
int pageIndxe = 1;
var secondPageNumbers = numbers.Skip(pageSize *pageIndxe).Take(pageSize);
First

  Frist返回集合内符合条件的第一个元素,在数据库操作中,常用于根据主键返回对象:

 C# Code 
1
Parent parent = parents.First(p = > p.ParentId == 1);
FirstOrDefault

  FirstOrDefault的功能和First一样,但First在集合内没有符合条件的元素的时候会抛出异常,而FirstOrDefault在这种情况下返回null。

C#在语法级别上对Linq的支持

  C#专门为Linq制定了一些的关键字来使Linq的表达方式更加优雅:

 C# Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/// <summary>
/// 父母
/// </summary>
public class Parent
{
    
/// <summary>
    /// 编号
    /// </summary>
    public int ParentId;
 
    
/// <summary>
    /// 姓名
    /// </summary>
    public string ParentName;
 
    
public Parent(int id, string name)
    {
        
this.ParentId = id;
        
this.ParentName = name;
    }
}
 
/// <summary>
/// 子女
/// </summary>
public class Child
{
    
/// <summary>
    /// 子女编号
    /// </summary>
    public int ChildId;
 
    
/// <summary>
    /// 父母编号
    /// </summary>
    public int ParentId;
 
    
/// <summary>
    /// 姓名
    /// </summary>
    public string ChildName;
 
    
public Child(int childId, int parentId, string name)
    {
        
this.ChildId = childId;
        
this.ParentId = parentId;
        
this.ChildName = name;
    }
}
 
Parent[] parents = { 
new Parent(1"张三"), new Parent(2"李四") };
Child[] children = { 
new Child(11"小红"), new Child(22"小明") };
 
var people1 = from child 
in children
              join parent 
in parents on child.ParentId equals parent.ParentId
              where child.ParentId > 
0 && child.ParentId < 100
              orderby child.ParentId, child.ChildId
              select 
new
              {
                  child.ChildName, parent.ParentName
              };
//等同于
var people2 = children.Where(c => c.ParentId > 0 && c.ParentId < 100)
              .OrderBy(c => c.ParentId)
              .ThenBy(c => c.ChildId)
              .Join(parents, c => c.ParentId, p => p.ParentId, (c, p) => 
new { c.ChildName, p.ParentName });

  从以上代码可以看出,使用关键字的查询语句比使用函数的表达方式更简洁和优雅,但使用函数式在多行代码拼凑组合时比较灵活。我自己觉得美中不足的是,很多Linq方法都没有制定成关键字,例如Count、Skip和Take等。

其他查询句法

  文本介绍的查询句法,在大多数场合中已经够用。但某些场合,我们会用到另外一些查询句法(我想特别是聚合查询),如果你对此感兴趣,可以查阅网上的相关资料,否则可以在用到的时候再去查:

http://www.cnblogs.com/zoupeiyang/archive/2011/06/25/2090004.html

Linq的缺陷

  我自己觉得Linq的缺陷主要在于Sql中的right outer join和full outer join在Linq里面并没有对应的查询方法,我不知道其他人或者微软有没有把这些看成缺陷,有没有打算在将来添加这两个功能。

Advertisements