扩展方法
很多.net框架标准查询结构的强大能力来源于扩展方法。事实上编译器将所有的查询综合直接转换为定义在此范围内的命名空间里的标准的查询操作符扩展方法。扩展方法是标记了定制属性的共享方法,并且它们被允许从实例方法语法中调用。事实上,扩展方法使用增加的方法扩展了已有的类型和构造的类型。
由于扩展方法主要是为库设计者准备的,Visual Basic并未提供直接的语言语法来支持声明它们,而是让作者直接在模块和成员上附上所需的定制属性来标明它们作为扩展方法,以下的例子定义了一个集合对象上的扩展方法Count:
<System.Runtime.CompilerServices.Extension> _
Module MyExtensions <System.Runtime.CompilerServices.Extension> _
Function Count(Of T)([Me] As IEnumerable(Of T)) As Integer
For Each Dim It In [Me]
Count += 1
Next
End Function
End Module
方括号是一个关键字转义,允许Me作为一个变量名存在。因为扩展方法是一个模仿实例方法的共享方法,使用标识符Me作为输入是很方便的,就好像我们在一个真实的实例方法里,但是它是一个关键字必须使用方括号转义,因此并不能真的被使用在共享方法里。扩展方法只是普通的共享方法,因此我们可以像Visual Basic中调用其他共享函数一样调用Count函数,只是提供所要操作的实例集合对象:
Dim TotalSmallCountries = _
MyExtensions.Count(Select Country _
From Country In Countries _
Where Country.Population < 1000000)
扩展方法通过普通的Import语句进行使用,这些扩展方法将会作为由第一个参数确定的类型的增加的方法出现:
Imports MyExtensions
Dim TotalSmallCountries = _
(Select Country _
From Country In Countries _
Where Country.Population < 1000000).Count()
扩展方法比普通的实例方法的优先级低,如果一个调用表达式的正常过程发现没有可用的实例方法,编译器将会试着将调用理解为一个扩展方法的调用。
使用这种查询的最自然的方法是,使用聚合语法,和我们前面看到的一样:
Dim TotalSmallCountries = _
Select Count(Country) _
From Country In Countries _
Where Country.Population < 1000000
嵌套函数
许多标准的查询操作符如Where,Select,SelectMany等,都是定义为扩展方法,并且使用类型为Func(Of S,T)的委托作为参数。为了使编译器将综合转化为查询操作符,或者为了VB程序员能够直接调用查询操作符,有必要更加容易的创建委托。特别的,我们需要能够创建closure,能够捕捉它们的上下文环境。Visual Basic创建closure的机制是通过嵌套本地函数和子程序声明保证的。
为展示嵌套式函数的用法,我们将会调用定义在System.Query命名空间里的查询操作符。其中一个扩展方法是TakeWhile函数,它从一个序列里提取元素只需要测试结果为真,然后跳开序列里剩下的部分。
<Extension> _
Shared Function TakeWhile(Of T) _
(source As IEnumerable(Of T), Predicate As Func(Of T, Boolean)) _
As IEnumerable(Of T)
OrderByDescending操作符对它的参数集合对象进行排序,并且按照排序关键字进行降序排序:
<Extension> _
Shared Function OrderByDescending (T, K As IComparable(Of K)) _
(Source As IEnumerable(Of T), KeySelector As Func(Of T, K)) _
As OrderedSequence(Of T)
另外一个查找到所有小国家的方法是,首先通过人口对它们进行排序,然后使用TakeWhile来挑选出人口少于100万的国家:
Function Population(Country As Country) As _
Integer Return Country.PopulationEnd FunctionFunction LessThanAMillion_
(Country As Country) As Boolean Return Country.Population <_
1000000End FunctionDim SmallCountries = _
Countries.OrderBy(AddresOf Population) _
.TakeWhile(AddresOf LessThanAMillion)
尽管不需要查询综合,VB可以支持匿名函数和子程序的直接语法(称为"lambda表达式"),编译器将此转化为本地函数声明。