首页 > 代码库 > 12.4.1 自定义查询表达式

12.4.1 自定义查询表达式

12.4.1 自定义查询表达式

 

原则上,我们可以使用查询处理任何类型,只要它提供了绑定操作。这是函数式编程中这类函数的标准名称,像上一节类型签名所展示的。从技术角度来讲,我们需要实现一些方法,在把查询表达式转换为标准的函数调用,由 C# 编译器所使用。我们将为 12.6 节中的 Option<T> 的类型实现这些方法,该类型没有实现 IEnumerable<T>,所以,不能使用标准查询运算符。

我们首先考虑一下,查询应用到选项类型,是什么意思。清单 12.15 有两个查询,左边的处理列表,右边的处理选项类型。我们将使用两个简单的函数来提供输入:ReadIntList 函数读取整数列表(类型为 List<int>),TryReadInt 返回选项值(类型为 Option<int>)。

 

清单 12.15 对列表和选项值使用查询 (C#)

 

var list = 

  from n in ReadIntList() 

  from m in ReadIntList() 

  select n * m;

var option = 

  from n in TryReadInt() 

  from m in TryReadInt() 

  select n * m;

 

除了处理的数据类型不同之外,查询完全相同,因此,它们使用不同的查询运算符实现。两者都读不同的输入,并返回输入整数的积。表 12.1 给出了样本输入和结果。

 

表 12.1 对于可能的不同输入,由使用列表和选项值的查询所产生的结果

Type of values

Input #1

Input #2

Output

Lists 

Options 

Options 

Options

[2; 3] 

Some(2) 

Some(3) 

None

[10; 100] 

Some(10) 

None 

Not required

[20; 200; 30; 300] 

Some(20) 

None 

None

 

对于列表,查询执行交叉联接运算(可以想象成 F# 序列表达式中的两个嵌套for 循环),会产生一个序列,由输入值的每种组合条目所组成;对于选项值,有三种可能性。

■当第一个输入是值时,需要读第二个。然后,根据第二个输入的不同,有以下两种情况:

— 如果第二个输入也是值,则结果是 Some 值,包含的结果是积。

— 如果第二个输入是 None,由于没有值相乘,因此,查询返回 None。

■当第一个输入为 None 时,我们知道,结果无需考虑第二输入。整个查询延迟执行,所以,不必去读第二输入:TryReadInt 函数只调用一次。

可以发现,查询表达式提供了一种简便的方法,处理选项值。清单 12.15 无疑要比我们在第六章看到的等价代码,容易写(和读),那时,我们显式地使用了高阶函数。在本章的后面我们将会看到,实现所有必要的查询运算符;不过,现在,我们还是先看一下在 F# 中的类似语法。

12.4.1 自定义查询表达式