`
terryfeng
  • 浏览: 507325 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

LINQ 概述和演变

阅读更多

LINQ 是.Net 3.5 中最重要的新功能,LinQ集成了C#编程语言中的查询语法,可以用相同的语法访问不同的数据源。LinQ提供了不同数据源的抽象层,所以可以使用相同的语法。

演变

下面是一组示例,这些示例都是基于一级方程式世界冠军。
获得国家是巴西的世界冠军,并按获胜的次数降序。

使用List<T>的查询

private static void TraditionalQuery()
        {
            //首先,需要用 GetChampions()静态方法获得对象列表。该列表放在泛型类
            //List<T>中。这个类的 FindAll()方法接收一个 Predicate<T>委托,该委托可以实现为一个匿名方法。只返回
            //Country  属性设置为 Brazil  的赛手。
            List<Racer> champions = new List<Racer>(Formula1.GetChampions());
            List<Racer> brazilChampions = champions.FindAll(
               delegate(Racer r)
               {
                   return r.Country == "Brazil";
               });

            //接着,用 Sort()方法给得到的列表排序。不应按照 Lastname  属性排序,
            //因为这是Racer  类的默认排序方式,而可以传送一个类型为 Comparison<T>的委托,该委托也实现为一个匿名
            //方法,来比较夺冠次数。使用 r2  对象,与 r1 比较,根据需要进行降序排序。

            brazilChampions.Sort(
               delegate(Racer r1, Racer r2)
               {
                   return r2.Wins.CompareTo(r1.Wins);
               });
            //foreach  语句最终迭代已排序的
            //集合中的所有 Racer  对象。
            foreach (Racer r in brazilChampions)
            {
                Console.WriteLine("{0:A}", r);
            }

        }

 

扩展方法

扩展方法可以将方法写入最初没有提供该方法的类中。还可以把方法添加到实现某个接口的任何类中(这句话的意思是说如果
你实现了某个接口的扩展方法,那么他的所有实现类也会具有此方法)
,这样多个类就可以使用相同的实现代码。

例如,String  类没有 Foo()方法。String 类是密封的(扩展方法什么都可以扩展),所以不能从这个类中继承。但可以执行一个扩展方
法,如下所示:
public static class StringExtension  //类的名字无所谓
{
public static void Foo(this string s)  //s只是方法局部变量没有特定含义
{
Console.WriteLine("Foo invoked for {0}", s);
}
}
扩展方法在静态类中声明定义为一个静态方法,其中第一个参数定义了它扩展的类型。Foo()方法扩展
了 string 类,因为它的第一个参数定义了 string  类型。为了区分扩展方法和一般的静态方法,扩展方法还需
要给第一个参数使用 this  关键字。

现在就可以使用带string 类型的 Foo()方法了:

 
string s = "Hello";  //s 是被扩展对象本身,同时又是被传递的参数
s.Foo();  //用被扩展类直接使用静态方法


结果在控制台上显示 Foo invoked for Hello,因为 Hello  是传送给 Foo()方法的字符串。
也许这看起来违反了面向对象的规则,因为给一个类型定义了新方法,但没有改变该类型。(这个思想得转变一会才行)
但实际上并非如此。扩展方法不能访问它扩展的类型的私有成员。调用扩展方法只是调用静态方法的一种新语法
对于字符串,可以用如下方式调用 Foo()方法,获得相同的结果:

string s = "Hello";
StringExtension.Foo(s); //用扩展类直接使用


要调用静态方法,应在类名的后面加上方法名。扩展方法是调用静态方法的另一种方式。不必提供定义了
静态方法的类名
,调用静态方法是因为它的参数类型,使用参数的类型在调用,而参数的值就调用对象本身的内容。
只需导入包含该类的命名空间,就可以将 Foo()扩展方法放在 string 类的作用域中,也就是说string 可以使用的地方。


定义 LINQ 扩展方法的一个类是 System.Linq  命名空间中的 Enumerable。只需导入这个命名空间,就打
开了这个类的扩展方法的作用域。为所有类提供扩展方法

下面列出了 Where()扩展方法的实现代码。Where()扩展方法的第一个参数包含了 this 关键字,

其类型是 IEnumerable<T>。这样,Where 方法就可以用于实现了 IEnumerable<T> 的每个类
型,(继承他的所有实现类和子类)。

为IEnumerable 扩展了Where方法

例如数组和 List<T> 实现了 IEnumerable<T>。
第二个参数是一个 Func<T,bool>委托,它引用了一个返回布尔值、参数类型为 T 的方法。
这个谓词在实现代码中被调用,用于实现谓词判断,也就是条件谓词,
这个谓词去实现
检查 IEnumerable<T>源中的项是否放在目标集合中。这个谓词当然是为了自己定义判断谓词准备的

委托中定义了谓词,程序中引用了这个委托,最后 yield return  语句就将源中的项返回给新的复合条件的目标列表序列枚举。


public static  IEnumerable<T> Where<T>(this IEnumerable<T>  source,  Func<T,  bool>  predicate)
{
foreach (T item in source)

if (predicate(item)) //谓词 
yield return item;
}
因为Where()实现为一个泛型方法,所以可以用于包含在集合中的任意类型。实现了 IEnumerable<T>的集
合都支持它。

 
提示:这里的扩展方法在程序集 System.Core  的 System.Linq  命名空间中定义。

现在就可以使用 Enumerable 类中的扩展方法 Where()、OrderbyDescending()和 Select()了。


这些方法都返回 IEnumerable<TSource>(源类型集合),所以可以使用前面的结果依次调用这些方法。
通过扩展方法的参数和使用定义了委托参数的实现代码的匿名方法。

 

private static void ExtensionMethods()
    {
        List<Racer> champions = new List<Racer>(Formula1.GetChampions());
        IEnumerable<Racer> brazilChampions = champions.Where(  //champions 是IEnumerable<T> 参数1
            //基于谓词筛选序列,源是brazilChampions,谓词条件是类型是Racer 值是Racer的r.Countray = "Brazil" 这个谓词条件
           delegate(Racer r)  //Racer 是匿名委托的第一个 T ,返回Bool,遍历每个元素时候所使用的判断方法委托,用于给其做判断时候调用,r,就是遍历中的每个元素
           {
               return r.Country == "Brazil";
           }).OrderByDescending( //对筛选后的集合,根据键值降序排序
           delegate(Racer r) //而这个委托是用于该方法遍历的时候进行调用比较时候所使用的条件式,使用那个属性
           {
               return r.Wins;
           }).Select( //重新生成集合给最终结果,当然如果不加这句完全没有问题
           delegate(Racer r)
           {
               return r;
           });
        //foreach  语句最终迭代已排序的
        //集合中的所有 Racer  对象。
        foreach (Racer r in brazilChampions)
        {
            Console.WriteLine("{0:A}", r);
        }

    }

 

 

 

λ表达式

 

C#  3.0  给匿名方法提供了一个新的语法——λ表达式。
使用λ表达式很容易的把匿名方法传送给 Where()、OrderbyDescending()和Select()方法。
这里把上面的例子改为使用λ表达式。这样语法比较短,也更容易理解了,
因为删除了return语句、参数类型和花括号。
λ表达式参见第 7  章。λ表达式在 LINQ 中非常重要,所以下面复习一下该语法。详细信息可参见第 7章。
比较λ表达式和匿名委托,会发现许多类似之处。

λ运算符=>的左边参数(左值)不需要添加参数类型,因为它们是由编译器解析的(就是扩展方法被扩展对象本身)。

λ运算符=>的右边参数(右值), 定义了执行代码(匿名委托中的谓词表达式)。

在匿名方法中,需要花括号和 return 语句(也就是=>右侧内容,表示参数类型及参数本身对应)。
在λ表达式中,不需要这些语法元素,因为它们是由编译器处理的。

如果λ运算符右边有多个语句,也可以使用花括号和return  语句。

提示:使用无参的 λ 表达式时,return 语句和花括号是可选的。但在 λ 表达式中仍可以使用这些语言结
构。详见第 7 章。

 

private static void LambdaExpressions()
       {
           List<Racer> champions = new List<Racer>(Formula1.GetChampions());
           IEnumerable<Racer> brazilChampions = champions.
              Where(r => r.Country == "Brazil"). // 还有点的,r 的类型被省略了由解析器负责,=>的左边和匿名委托中执行的语句一样但是return省略了
              OrderByDescending(r => r.Wins). //参照匿名委托
              Select(r => r);

           foreach (Racer r in brazilChampions)
           {
               Console.WriteLine("{0:A}", r);
           }
       }

 

 

 

LINQ 查询

 

最后一个需要修改的是用新的LINQ  查询记号定义查询。
语句

from  r  in  Formula1.GetChampions()

Where r.Country  ==  "Brazil"

orderby  r.Wins  descending 

select  r;  //虽然分行了,但还是表示只有一句,只有一个分号

是一个 LINQ  查询,子句 from、where、orderby、descending 和 select  都是这个查询中的预定义关键字。

编译器把这些子句映射到扩展方法上。这里的语法是
使用扩展方法 Where()、Orderby- Descending()和 Select()。λ表达式传送给参数。

Where r.Country == "Brazil" 转换到 Where(r => r.Country == "Brazil") (Lambda表达式表示的扩展方法)
.orderby r.Wins descending 转换到 OrderByDescending(r => r.Wins)

LinQ把方法本身的括号也去掉了,点也去掉了

提示:LINQ 查询是 C#语言中的一个简化查询记号,

编译器会编译查询表达式,调用扩展方法。查询表达式只是 C# 中的一个语法,但不需要修改底层的 IL  代码。
1.查询表达式必须以from  子句开头,以 select 或 group  子句结束。

2.在这两个子句之间,可以使用 where、orderby、join、let  和其他 from  子句。

private static void LinqQuery()
        {
            List<Racer> champions = new List<Racer>(Formula1.GetChampions());
            var query = from r in Formula1.GetChampions()  //from 开始
                        where r.Country == "Brazil"
                        orderby r.Wins descending
                        select r; //select 结尾

            foreach (Racer r in query)
            {
                Console.WriteLine("{0:A}", r);
            }
        }

 

注意,变量 query  只指定了 LINQ  查询。

该查询不是通过这个赋值语句执行的,只要使用 foreach  循环访问查询,该查询就会执行。详见后面的内容。

意思就是说,Linq就好象一个方法一样,而query就好象调用这个方法的调用方法,调用就会执行。而不是执行一次获得了结果。


在前面的示例中,学习了 C# 3.0 语言特性,以及它们与 LINQ  查询的关系。下面深入探讨 LINQ  的特性。

推迟查询的执行

在运行期间定义查询表达式时,查询就不会运行。查询会在迭代数据项时运行。

意思是说Linq查询语句被定义了,并没有执行,而是在调用和使用这个Var 结果的时候才去调用,所以在定查询语句之后的期间,修改了结果集,那么结果还是会在最新的结果集状态下发生做用


再看看扩展方法 Where()。它使用 yield return 语句返回谓词为true  的元素。因为使用了 yieldreturn 语句,
所以编译器会创建一个枚举器,在访问枚举中的项后,就返回它们。

 
这是一个非常有趣、也非常重要的结果。在下面的例子中,创建了一个 String  元素集合,用名称 names 填
充它。接着定义一个查询,从集合中找出以字母 J 开头的所有名称。集合也应是排好序的。在定义查询时,
不会执行迭代。
相反,迭代在 foreach  语句中进行,迭代所有的项。集合中只有一个元素 Juan  满足 where 表
达式的要求,即以字母 J  开头。迭代完成后,将 Juan 写入控制台。之后在集合中添加四个新名称,再次执行
迭代。 结果是添加后结果后的新运算结果

 

private static void DeferQuery1()
{


    List<string> names = new List<string> { "Nino", "Alberto", "Juan", "Mike", "Phil" };
    var namesWithJ = from n in names
                     where n.StartsWith("J")   //查询开始部分匹配的
                     orderby n
                     select n;

    Console.WriteLine("First iteration");
    foreach (string name in namesWithJ)
    {
        Console.WriteLine(name);
    }
    Console.WriteLine();

    names.Add("John");
    names.Add("Jim");
    names.Add("Jack");
    names.Add("Denny");

    Console.WriteLine("Second iteration");
    foreach (string name in namesWithJ) //再次调用
    {
        Console.WriteLine(name);
    }

}
image 
因为迭代在查询定义时不会执行,而是在执行每个 foreach  语句时执行,所以可以看到其中的变化,如应
用程序的结果所示
 
重点:每次使用Var 定义的 namesWithJ  结果集的时候都会调用查询,就好象是一个方法调用
如果这不是你要的结果可以调用扩展方法 ToArray()、ToEnumerable()、ToList()等可以改变这个操作。
 
在示例中,扩展方法ToList()  迭代集合,返回一个实现了 IList<string>的集合。
 
这样即使在修改结果集,再使用结果变量也不会再调用查询了,这回返回的就是一个切实的结果。
 
private static void DeferQuery2()
{


    List<string> names = new List<string> { "Nino", "Alberto", "Juan", "Mike", "Phil" };
    IList<string> namesWithJ = (from n in names  //返回IList
                                where n.StartsWith("J")
                                orderby n
                                select n).ToList();

    Console.WriteLine("First iteration");
    foreach (string name in namesWithJ)
    {
        Console.WriteLine(name);
    }
    Console.WriteLine();

    names.Add("John");
    names.Add("Jim");
    names.Add("Jack");
    names.Add("Denny");

    Console.WriteLine("Second iteration");
    foreach (string name in namesWithJ) //这回不回调用了因为已经是结果集
    {
        Console.WriteLine(name);
    } 

}

 

image

在结果中可以看到,两次迭代中输出保持不变,但集合中的值改变了:

参考:

C#高级编程 第六版 

源代码下载:LINQSamples.rar

分享到:
评论

相关推荐

    C# linq 基础 PPT

    首先,LINQ概述中提到了它的两个主要作用。第一,作为数据和编程语言的桥梁,LINQ允许开发者使用相同的查询语法来操作各种数据源,如对象、XML、SQL Server等,通过Linq to Object、Linq to Xml、Linq To Sql等方式...

    .NET技术及C#语言发展史与应用场景综述

    内容概要:本文全面概述了.NET技术及其发展历史,特别是C#语言的演变历程。从C# 1.0 到 C# 7 和 .NET Core 2.0,涵盖了.NET Framework和.NET Core的主要版本及其特性。文中详细讲解了各个版本的重要特性和改进,如...

    C#课件-C#概述NET Framework的介绍

    C# 还引入了泛型、匿名方法、LINQ(Language Integrated Query)等高级特性,进一步提高了开发效率和代码质量。 Visual Studio 2005.NET 是微软的旗舰级集成开发环境(IDE),它集成了对 C# 的支持,提供了丰富的...

    博客园c#文章精华2

    通过以上内容的梳理,可以看出这些知识点涵盖了C#编程的各个方面,从集合类的应用到数据库操作,再到高级特性如LINQ和泛型等,对于希望深入学习C#及其在.NET框架中应用的开发者来说,是非常宝贵的资料。

    ASP.NET 3.5 开发大全

    - **LINQ概述:** LINQ to Objects、LINQ to SQL等。 - **查询表达式:** 使用LINQ查询表达式进行数据操作。 - **第21章 使用LINQ 查询** - **数据查询:** 如何使用LINQ查询数据。 - **LINQ与数据库:** LINQ...

    Programming C# 3.0

    - **LINQ(Language Integrated Query)**:深入探讨LINQ的使用方法,包括查询表达式、LINQ to Objects、LINQ to SQL等,帮助开发者更高效地处理数据集合。 5. **.NET 3.5框架** - **WPF**:介绍Windows ...

    ASP.NET Web程序设计教程

    首先,第一章“ASP.NET概述”可能会介绍ASP.NET的基本概念和技术架构。ASP.NET是微软开发的一种服务器端Web应用框架,用于构建动态网站、Web应用和Web服务。它基于.NET Framework,提供了丰富的控件、强大的数据绑定...

    Entity framework4 实战教程

    - 数据访问技术的历史沿革,包括传统 ADO.NET 和 LINQ to SQL 等方法。 - EF 如何简化数据访问过程,提高开发效率。 - EF3 相比早期版本的进步之处。 #### 三、入门 Entity Framework 3 - **章节内容**:第二章...

    c#编程从基础到应用

    它由C和C++演变而来,拥有强大的类型安全性、异常处理以及垃圾回收机制。C#设计之初就与.NET框架紧密集成,因此它可以用来开发多种类型的应用程序,包括Windows桌面应用、Web应用、Web服务和移动应用等。 ### C# ...

    基于.net的数据库系统的设计与实现.doc

    .NET框架不断更新,引入了诸如LINQ(Language Integrated Query)、WCF(Windows Communication Foundation)和WF(Windows Workflow Foundation)等新特性,增强了开发效率和应用程序的功能。 2.2 ASP.NET ASP...

    ASP.NET 2.0(PDF版)

    此外,数据绑定机制的增强使得控件与数据源之间的交互更为简洁,如Linq-to-SQL和ObjectDataSource。 四、页面生命周期与事件模型 ASP.NET 2.0的页面生命周期包括初始化、加载、验证、呈现和卸载等阶段。理解这些...

    Getting Started with ASP.NET 4.5 Web Forms - Beta

    了解它的起源和演变将有助于更好地理解当前版本的功能。 #### Web 应用程序模板中的功能 本节将介绍 ASP.NET 4.5 Web Forms 应用程序模板中包含的一些主要功能,如页面生命周期、事件处理机制等。 #### Visual ...

    数据结构(C#语言版)

    - **教材语言演变**: 早期使用汇编语言,随后PASCAL、C、C++、Java等语言被广泛应用于数据结构的教学中。 - **C#语言的地位**: C#作为微软.NET框架中的主要语言之一,以其简洁、高效的特点逐渐成为数据结构教学的新...

    rx.net in action

    - **响应式编程的历史**:简要回顾响应式编程的发展历程,以及它如何演变成为现代软件开发中的重要工具。 - **响应式编程的优势**:详细探讨响应式编程相对于传统编程范式的优点,如更好的错误处理、更简洁的代码等...

    DataObjects.Net-RAD的ORM框架-简介

    **DataObjects.Net - RAD ORM框架概述** DataObjects.Net是一款针对.NET平台的快速应用程序开发(RAD)对象关系映射(ORM)框架。它旨在简化数据库访问,提高开发效率,同时保持高度的灵活性和性能。ORM框架将...

    Apress Pro ASP.NET MVC 3 Framework(2011-07-09)

    - 包括Lambda表达式、LINQ查询语法、匿名类型等高级特性,这些可以帮助开发者编写更加简洁和可读性高的代码。 - **第6章:MVC必需的工具** - 探讨了开发过程中常用的工具和技术,如单元测试框架、调试工具等。 -...

Global site tag (gtag.js) - Google Analytics