导航

LINQ 初级

Posted on 2010-01-26 16:36  小荷才露尖尖角  阅读(464)  评论(1编辑  收藏  举报

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() 这时只能采用以下替代方案

查询方法

查询语句 + 查询方法的混合方式

一般情况下,建议使用可读性更好的查询语句

高级查询方法

聚合类

CountMax/MinAverage

排序类

ThenBy

分区类

TakeTakeWhileSkipSkipWhile

集合类

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

}

 

输出结果为3skipWhile先跳过12 我们得到3, 4, 5, 6, 7, 8, 9  TaKeWhile 提取项是取到3 当到4时不满足条件不会被输出同时停止执行

 

使用生成类查询方法时,需要注意以下几点:

和其他几类方法不同,Range/Repeat 不是扩展方法,而是普通的静态方法

Range 只能产生整数序列

Repeat 可以产生泛型序列

所有的查询方法都存放在 System.Linq.Enumerable 静态类中