LINQ中的Lambda表达式

摘要:
右边的操作数是一个lambda表达式,用于检查客户的name属性是否等于“Donna”。此lambda表达式将传递到Where方法中,并将对客户列表中的每个客户执行比较操作。使用扩展方法定义的查询称为基于方法的查询。其次,dNames的类型是Inumberable<string>;我们使用关键字var来使用编译器的新函数推断它。

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查询。

免责声明:文章转载自《LINQ中的Lambda表达式》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇点云下采样2Java常用的数组排序算法(面试宝典)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

XML、Linq、序列化、反射、XSLT在测试报告统计工具中的应用 Anny

客户要求对XML格式的测试结果进行统计,并发送出特定格式的邮件。 1. 测试结果有两种文件,一种是以xml为后缀的xml格式文件,一种是VS里跑测试用例产生的以trx为后缀的xml格式文件。 2. 每个文件里包含多个测试用例,每个测试用例有对应的Owner和归属类别Area。 3. 最后输出的报告格式如下: Area File Owner Pass F...

Java List集合 遍历 四种方式(包含 Lambda 表达式遍历)

示例代码如下: package com.miracle.luna.lambda; import java.util.ArrayList; import java.util.List; /** * @Author Miracle Luna * @Date 2019/6/9 23:36 * @Version 1.0 */ pub...

python tkinter 学生信息管理系统

使用tkinter模块,python3.6,主要功能有添加,查询,删除,修改学生信息 使用模版: 1 from tkinter import * 2 import tkinter.font as tkFont 3 import tkinter as tk 4 from tkinter import ttk 最主要也是最难做的是,实现不同功能的界面在同一TK...

C# 动态Linq(结合反射)

    这篇文章决定对最近一个单机版Web程序用到的东西总结一下。 一、反射Linq之OrderBy  动态Linq结合反射对某字段排序: namespace 动态Linq { class Program { static void Main(string[] args) { L...

.NetCore 使用 Linq 动态拼接Expression表达式条件来实现 对EF、EF Core 扩展查询排序操作

相信在使用EF的时候对查询条件或者排序上的处理令人心烦,下面我们就来动态拼接表达式解决这一问题 当我们在查询中使用Where的时候可以看到如下参数 下面我们就来扩展 Expression<Func<T,bool>> 这个参数 第一步: 建立处理功能类 首先我们要创建一个查询条件转化为表达式的泛型功能类 如UosoExpressio...

Java之lambda表达式

一、lambda表达式的写法 packagetest; //构造线程的两种方式:1、实现Runnable接口 2、继承Thread类 public classTest14 { public static voidmain(String[] args) { Thread thread = new Thread(newMyThrea...