zoukankan      html  css  js  c++  java
  • LINQ中的Lambda表达式

    LINQ中的Lambda表达式(1)

    Lambda Expressions in LINQ

    在第12章,我提到可以用lambda表达式定义内联的委托定义。在如下表达式中:

    customer => customer.FirstName == "Donna"

    左边的操作数,customer,是输入参数。右边的操作数是lambda表达式,检查客户的名字属性是否等于"Donna"。因此,对于给定的客户对象,你再检查它的名字是否为Donna。

    这个lambda表达式会被传入Where方法并对在客户列表中的每一个客户执行这个比较操作。

    使用扩展方法定义的查询被称为基于方法的查询(method-based queries)。虽然查询和方法的语法不同,它们的语义相同,编译器会把它们转变为相同的IL代码。你可以根据自己的喜好使用其中之一。

    让我们以一个简单的查询开始,如示例13-8所示。

    示例13-8:一个简单的基于方法的查询

    using System;
    using System.Linq;
    namespace SimpleLamda
    {
    class Program
    {
    static void Main(string[] args)
    {
          string[] names = { "Jesse", "Donald", "Douglas" };
    var dNames = names.Where(n => n.StartsWith("D"));
    foreach (string foundName in dNames)
    {
    Console.WriteLine("Found: " + foundName);
    }
         }
    }
    }
    Output:
    Found: Donald
    Found: Douglas

    语句names.Where是

    System.Linq.Enumerable.Where(names,n=>n.StartsWith("D"));

    的一个缩写。

    Where是一个扩展方法,因此你可以把对象(names)作为第一个参数传入。通过包含名空间System.Linq,你可以直接对names对象调用Where而不是通过Enumerable。

    其次,dNames的类型是Ienumberable<string>;我们通过关键字var来使用编译器新的功能对其进行推断(infer)。当然这样做不会损害类型安全,因为通过推断var被编译为类型Ienumerable<string>。

    因此你可以把:

    var dNames = names.Where(n => n.StartsWith("D"));

    这行代码理解为"从集合names中找出以字母D开头的成员,然后填充到IEnumerable集合中"。

    因为方法的语法和C#编译器如何处理查询更接近,值得花一些时间来看看一个更复杂的查询是如何描述的,从而增长对LINQ的理解。让我们把示例13-3翻译成一个基于方法的查询来看看它是怎样的(参见示例13-9)。

    示例13-9:使用方法语法的复杂查询

    namespace Programming_CSharp
    {
    // 简单客户类
    public class Customer
    {
    // 和示例13-1相同
    }
         // 客户地址类
    public class Address
    {
    // 和示例13-3相同
    }
         // 主程序
    public class Tester
    {
    static void Main()
    {
    List<Customer> customers = CreateCustomerList();
    List<Address> addresses = CreateAddressList();
                   var result = customers.Join(addresses,
    customer => string.Format("{0} {1}", customer.FirstName,
    customer.LastName),
    address => address.Name,
    (customer, address) => new { Customer = customer, Address =
    address })
    .OrderBy(ca => ca.Customer.LastName)
    .ThenByDescending(ca => ca.Address.Street);
                   foreach (var ca in result)
    {
    Console.WriteLine(string.Format("{0}\nAddress: {1}",
    ca.Customer, ca.Address));
    }
    }
            // 使用相同数据创建客户列表
    private static List<Customer> CreateCustomerList()
    {
    // 和示例13-3相同
    }

    LINQ中的Lambda表达式(2)

    示例13-9:使用方法语法的复杂查询(续例)

            // 使用相同数据创建客户列表
    private static List<Address> CreateAddressList()
    {
    // 和示例13-3相同
    }
    }
    }

    Output:
    Janet Gates
    Email:   janet1@adventure-works.com
    Address: 800 Interchange Blvd., Austin
    Janet Gates
    Email:   janet1@adventure-works.com
    Address: 165 North Main, Austin
    Orlando Gee
    Email:   orlando0@adventure-works.com
    Address: 2251 Elliot Avenue, Seattle
    Keith Harris
    Email:   keith0@adventure-works.com
    Address: 7943 Walnut Ave, Renton
    Keith Harris
    Email:   keith0@adventure-works.com
    Address: 3207 S Grady Way, Renton

    在示例13-3中,查询使用了查询的语法:

    var result =
    from   customer in customers
    join address in addresses on
    string.Format("{0} {1}", customer.FirstName, customer.LastName)
    equals address.Name
    orderby customer.LastName, address.Street descending
    select new { Customer = customer, Address = address.Street };

    它被翻译为以下方法的语法:

    var result = customers.Join(addresses,
    customer => string.Format("{0} {1}",
    customer.FirstName,
    customer.LastName),
    address => address.Name,
    (customer, address) => new { Customer =
    customer, Address = address })
    .OrderBy(ca => ca.Customer.LastName)
    .ThenByDescending(ca => ca.Address.Street);

    lambda表达式需要一些时间来适应。以OrderBy子句开始;你可以把它读作"通过以下方式来排序:对于每一个客户地址,获得客户的姓氏。"你把整个语句读作:"从客户开始,和地址通过以下方式连接:连接客户的名字和姓氏,获取地址的名称,对两者进行连接,然后对于每一个结果记录创建一个客户地址对象,这个对象的客户和地址由取出来的客户和地址赋值;然后首先通过每个客户的姓氏排序,再接着根据每个地址的街道名称按降序排列。"

    主要的数据源即客户集合,仍然是主要的目标对象。扩展方法Join()作用于它来执行连接操作。它的第一个参数是第二个数据源地址。接下来的两个参数是每个数据源的连接条件域。最后一个参数是连接条件的结果,实际上是查询的选择子句。

    查询表达式的OrderBy子句表明你想将客户姓氏按升序排列,然后将它们的街道地址按降序排列。在方法语法中必须通过使用OrderBy和ThenBy方法指明这个顺序。

    也可以只调用一系列的OrderBy方法,但是这些方法必须逆序调用。也就是说你必须在查询的OrderBy序列中首先对最后一个域调用这个方法,最后才对第一个域调用这个方法。在本例中,你须要首先调用对街道的排序,然后才能调用对名称的排序:

    var result = customers.Join(addresses,
    customer => string.Format("{0} {1}", customer.FirstName,
    customer.LastName),
    address => address.Name,
    (customer, address) => new { Customer =
    customer, Address = address })
    .OrderByDescending(ca => ca.Address.Street)
    .OrderBy(ca => ca.Customer.LastName);


    从结果可以看出,两个例子的输出是一样的。因此你可以根据自己的喜好选择其中一个。

    提示:Ian Griffiths,地球上最聪明的C#程序员之一,(他的blog在IanG On Tap上,(http://www.interact-sw.co.uk/iangblog/)阐述了以下的观点,我也将会在第15章演示这个观点, 但是我想在这里先表明:"你可以在许多不同的源上使用完全相同的这两个语法,但是行为并不总是相同的。一个lambda表达式的意义随着传给它的函数的原型不同而不同。在这些例子中,它是委托的一个简洁的语法。但是如果你对一个SQL数据源使用相同的查询格式,lambda表达式将会被转变为另外的东西。"

    所有的LINQ扩展方法--连接(Join)、选择(Select)、Where,以及其他--具有多种实现,每个实现面向不同的目标类型。这里我们学习的是在IEnumerable上操作的方法。与在IQueryable上操作的方法有微妙的不同。它们接受表达式而不是接受连接、映射、Where及其他子句的委托。这些是非常神奇的技术,使得C#源代码能够转换为相应的SQL查询。

  • 相关阅读:
    centos7 安装prometheus node_exporter
    RMAN备份演练初级篇
    RMAN命令
    oracle数据库的归档模式
    oracle的会话(session)
    oracle的例程
    oracle热备份
    Oracle数据库归档模式的切换及其相关操作详解
    Oracle角色
    类名.class, class.forName(), getClass()区别
  • 原文地址:https://www.cnblogs.com/zhcw/p/2205294.html
Copyright © 2011-2022 走看看