Lambda 表达式 Lambda Expressions (Visual Basic)

Lambda 表达式 Lambda Expressions (Visual Basic)

Lambda 表达式是一个不带名字的函数(function)或子过程(sub),可以用在任何接收委托的地方。Lambda 表达式可以是函数,也可以是子过程,可以是单行的,也可以写成多行。您可以把当前范围的值传递给 lambda 表达式。

注意,RemoveHandler 语句是一个例外,您不可以给委托参数传入一个 lambda 表达式

您使用 Function 或 Sub 关键字创建 lambda 表达式,就像创建普通函数和子过程一样。不过,lambda 表达式包含在一个语句中。

下面例子是一个 lambda 表达式,将参数自增 1 后返回。此例子展示单行和多行的 function 式 lambda 表达式语法

Dim  increment1  =   Function (x) x  +   1
Dim  increment2  =   Function (x)
Return x 
+   2
End Function
 
'  Write the value 2.
Console.WriteLine(increment1( 1 ))
 
'  Write the value 4.
Console.WriteLine(increment2( 2 ))

 

下面例子是一个 lambda 表达式,演示子过程的单行和多行 lambda 表达式语法,把值写到控制台。

Dim  writeline1  =   Sub (x) Console.WriteLine(x)
Dim  writeline2  =   Sub (x)
Console.WriteLine(x)
End Sub
 
'  Write "Hello".
writeline1( " Hello " )
 
'  Write "World"
writeline2( " World " )

 

注意到前面的例子中,lambda 表达式赋值给了变量。当您引用该变量时,就调用该 lambda 表达式。您还可以声明 lambda 表达式的同时调用它,看下面的例子。

Console.WriteLine(( Function (num  As   Integer ) num  +   1 )( 5 ))
lambda 表达式可以作为函数的返回值(例子在本文后面的 上下文章节 中),或者作为实参传送给委托类型的参数,像下面列子一样。
Module Module2
 
Sub  Main()
'  下面代码会打印 Success, 因为 4 是偶数.
testResult( 4 Function (num) num  Mod   2   =   0 )
 
'  下面代码会打印 Failure, 因为 5 不大于 10.
testResult( 5 Function (num) num  >   10 )
End Sub
 
'  Sub testResult 接收两个参数,一个 integer 和一个 function 委托,该委托接收 integer 值返回 boolean 值。
'
 如果函数接收 integer 的实参并返回 True,则输出 Success,否则输出 Failure。
 
Sub  testResult(ByVal value  As   Integer , ByVal fun  As  Func(Of  Integer Boolean ))
If  fun(value)  Then
Console.WriteLine(
" Success " )
Else
Console.WriteLine(
" Failure " )
End   If
End Sub
 
End  Module

 

Lambda 表达式语法

Lambda 的语法类似于标准的函数或子过程,区别如下:

  • Lambda 表达式没有名字
  • Lambda 表达式没有修饰符(modifier),如 Overloads 或 Overrides
  • 单行 lambda 函数的返回值类型不使用 As 子句标识,而是从 lambda 表达式主体计算所得值中推断。例如,lambda 表达式的主体是 cust.City = "London",则其返回值类型是 Boolean。
  • 多行 lambda 函数,可以使用 As 子句标识返回类型,或者省略掉 As 子句而自动推断返回类型。当多行 lambda 函数省略掉 As 子句,返回值的类型推断为所有 Return 子句的主导类型。主导类型是数组文本中所有其它类型可以扩大到的唯一类型。如果这个唯一类型不能决定,则主导类型是 Object。例如,返回值列表提供给数组文本包含的值类型有 Integer、Long、Double,则最终数组是 Double 类型。Integer 和 Long 扩大为 Double,且唯一是 Double。这样,Double 就是主导类型。查看详细,请看 Widening and Narrowing Conversions。
  • 单行函数的主体必须是一个有返回值的表达式,而不是一个语句。单行函数没有 Return 语句,其返回值是函数体中表达式的值。
  • 单行子过程的主体必须是一个单行语句。
  • 单行函数或子过程没有 End Function 或 End Sub 语句。
  • 您可以用 As 关键字标识 lambda 表达式参数的类型,或者自动推断该类型。要么所有参数类型都标识,要是所有都自动推断。
  • Optional 和 Paramarray 参数不被允许
  • 泛型参数不被允许

 

上下文

lambda 表达式跟定义它的上下文共享作用范围。它跟容纳范围(containing scope)的内部的其它代码有相同的访问权限,包括访问成员变量、函数或子过程、Me、参数、和本地变量。

访问容纳范围(containing scope)的本地变量和参数可以超出该范围的生命期。只要委托引用的 lambda 表达式不被垃圾收集器收集,就可以访问保持的原始环境的变量。在下面的例子中,变量 target 是 makeTheGame 的本地变量,lambda 表达式 playTheGame 定义在 makeTheGame 中。注意到作为返回值的 lambda 表达式,在 Main子过程中赋值给 takeAGuess,仍然可以访问本地变量 target。

Module Module6
 
Sub  Main()
' 变量 takeAGuess 是一个 Boolean 函数,它存储在 makeTheGame 中设置的目标数字
Dim  takeAGuess  As  gameDelegate  =  makeTheGame()
 
'  设置循环来玩游戏
Dim  guess  As   Integer
Dim  gameOver  =   False
While   Not  gameOver
guess 
=   CInt ( InputBox ( " Enter a number between 1 and 10 (0 to quit) " " Guessing Game " " 0 " ))
'  输入 0 表示退出游戏
If  guess  =   0   Then
gameOver 
=   True
Else
    
'  测试您的猜测,并告知您是否正确。takeAGuess 方法因不同的猜测值而被多次调用
     '  Main 过程访问不了目标值,且它没有被传入。
gameOver  =  takeAGuess(guess)
Console.WriteLine(
" Guess of  "   &  guess  &   "  is  "   &  gameOver)
End   If
End   While
 
End Sub
 
Delegate 
Function  gameDelegate(ByVal aGuess  As   Integer As   Boolean
 
Public   Function  makeTheGame()  As  gameDelegate
 
'  生成目标数字,介于 1 和 10。注意到 target 是一个本地变量,
'
 当从 makeTheGame 返回后,它就不能被直接访问了。
Randomize ()
Dim  target  As   Integer   =   CInt ( Int ( 10   *   Rnd ()  +   1 ))
 
'  Print the answer if you want to be sure the game is not cheating
'
 by changing the target at each guess.
Console.WriteLine( " (Peeking at the answer) The target is  "   &  target)
 
    
'  本游戏返回 lambda 表达式,该 lambda 表达式承载了创建它的上下文的环境,
     '  这个环境包含 target 变量(target number)。注意到仅有当前猜测(current guess)
     '  才是返回 lambda 表达式的参数,而不是 target。
 
'  猜测值跟目标值是否匹配
Dim  playTheGame  =   Function (guess  As   Integer ) guess  =  target
 
Return playTheGame
 
End Function
 
End  Module

 

下面例子演示嵌套 lambda 表达式的大范围访问权。当作为返回值的 lambda 表达式在 Main 中被调用(aDel),它访问这些元素。

  • 定义它的类的字段:aField
  • 定义它的类的属性:aProp
  • 定义它的方法 functionWithNestedLambda 的参数: level1
  • functionWithNestedLambda 的本地变量:lovalVar
  • 外部 lambda 表达式的参数:level2
Module Module3
 
Sub  Main()
 
    
'  创建类的一个实例,属性值 Prop 设为 1
     Dim  lambdaScopeDemoInstance  =
        
New  LambdaScopeDemoClass  With  {.Prop  =   1 }
 
    
'  变量 aDel 被绑定到 functionWithNestedLambda 返回的嵌套的 lambda 表达式
     Dim  aDel  As  aDelegate  =
        lambdaScopeDemoInstance.functionWithNestedLambda(
2 )
 
    
'  现在返回的嵌套的 lambda 表达式被调用,4 变成参数 level3 的值
    Console.WriteLine( " First value returned by aDel:  "   &  aDel( 4 ))
 
    
'  改变某些值,以验证 lambda 表达式是否可访问变量,不仅他们的原始值
    lambdaScopeDemoInstance.aField  =   20
    lambdaScopeDemoInstance.Prop 
=   30
    Console.WriteLine(
" Second value returned by aDel:  "   &  aDel( 40 ))
End Sub
 
Delegate 
Function  aDelegate(
    ByVal delParameter 
As   Integer As   Integer
 
Public  Class LambdaScopeDemoClass
    
Public  aField  As   Integer   =   6
    
Dim  aProp  As   Integer
 
    
Property  Prop()  As   Integer
        
Get
            Return aProp
        
End   Get
        
Set (ByVal value  As   Integer )
            aProp 
=  value
        
End   Set
    
End Property
 
    
Public   Function  functionWithNestedLambda(
            ByVal level1 
As   Integer As  aDelegate
 
        
Dim  localVar  As   Integer   =   5
 
        
'  当嵌套的 lambda 表达式作为 Main 的 aDel 第一次运行,变量有这些值:
         '  level1 = 2
         '  level2 = 3, Return 语句中,调用 aLambda 后
         '  level3 = 4, aDel 在 Main 中调用后
         '  locarVar = 5
         '  aField = 6
         '  aProp = 1
         '  第二次执行, 更改了两个变量:
         '  aField = 20
         '  aProp = 30
         '  level3 = 40
         Dim  aLambda  =   Function (level2  As   Integer ) _
            
Function (level3  As   Integer ) _
                level1 
+  level2  +  level3  +  localVar  +  aField  +  aProp
        

         
' 函数返回嵌套的 lambda,3 作为参数 level2 的值
        Return aLambda( 3 )
    
End Function
 
End  Class
End  Module

 

转换到委托类型

一个 lambda 表达式可以隐式地转换为兼容的委托类型。有关兼容性的普遍问题(general requirements for compatibility),请看 Relaxed Delegate Conversion。例如,下面代码展示一个 lambda 表达式隐式转换为匹配的委托签名 Func(Of Integer, Boolean)。

'  明确地声明一个委托
Delegate  Function  MultipleOfTen(ByVal num  As   Integer As   Boolean
 
'  这个函数匹配委托
Function  IsMultipleOfTen(ByVal num  As   Integer As   Boolean
    Return num 
Mod   10   =   0
End Function
 
'  这个方法接收一个委托类型的输入参数,checkDelegate 参数也可以是类型 Func(Of Integer, Boolean)
Sub  CheckForMultipleOfTen(ByVal values  As   Integer (),
        ByRef checkDelegate 
As  MultipleOfTen)
    
For   Each  value In values
        
If  checkDelegate(value)  Then
            Console.WriteLine(value 
&   "  is a multiple of ten. " )
        
Else
            Console.WriteLine(value 
&   "  is not a multiple of ten. " )
        
End   If
    
Next
End Sub
 
 
'  这个方法演示显式地定义的委托(explicitly defined delegate)和 lambda 表达式传给同样的输入参数。
Sub  CheckValues()
    
Dim  values  =  { 5 10 11 20 40 30 100 3 }
    CheckForMultipleOfTen(values, AddressOf IsMultipleOfTen)
    CheckForMultipleOfTen(values, 
Function (num) num  Mod   10   =   0 )
End Sub

 

下面代码演示 lambda 表达式隐式地转换为匹配的委托签名 Sub(Of Double, String, Double)。

Module Module1
    Delegate 
Sub  StoreCalculation(ByVal value  As   Double ,
        ByVal calcType 
As   String ,
        ByVal result 
As   Double )
 
    
Sub  Main()
        
'  创建 DataTable 存储信息
         Dim  valuesTable  =   New  DataTable( " Calculations " )
        valuesTable.Columns.Add(
" Value " , GetType( Double ))
        valuesTable.Columns.Add(
" Calculation " , GetType( String ))
        valuesTable.Columns.Add(
" Result " , GetType( Double ))
 
        
'  定义 lambda 子过程写信息到 DataTable.
         Dim  writeToValuesTable  =   Sub (value  As   Double , calcType  As   String , result  As   Double )
            
Dim  row  =  valuesTable.NewRow()
            row(
0 =  value
            row(
1 =  calcType
            row(
2 =  result
            valuesTable.Rows.Add(row)
        
End Sub
 
        
'  定义原值
         Dim  s  =  { 1 2 3 4 5 6 7 8 9 }
 
        
'  执行计算
         Array .ForEach(s,  Sub (c) CalculateSquare(c, writeToValuesTable))
        
Array .ForEach(s,  Sub (c) CalculateSquareRoot(c, writeToValuesTable))
 
        
'  显示数据
        Console.WriteLine( " Value "   &  vbTab  &   " Calculation "   &  vbTab  &   " Result " )
        
For   Each  row  As  DataRow In valuesTable.Rows
            Console.WriteLine(row(
0 ).ToString()  &  vbTab  &
            row(
1 ).ToString()  &  vbTab  &
            row(
2 ).ToString())
        
Next
 
    
End Sub
 
 
    
Sub  CalculateSquare(ByVal number  As   Double , ByVal writeTo  As  StoreCalculation)
        writeTo(number, 
" Square  " , number  ^   2 )
    
End Sub
 
    
Sub  CalculateSquareRoot(ByVal number  As   Double , ByVal writeTo  As  StoreCalculation)
        writeTo(number, 
" Square Root " , Math.Sqrt(number))
    
End Sub
End  Module

 

  

当您将 lambda 表达式赋值给委托或者将它们作为实参传给过程时,您可以标识参数名称,但省略他们的数据类型,让类型从委托中取。

 

例子

下面例子定义一个 lambda 表达式,如果 nullable 参数被赋值则返回 True,否则返回 False。

Dim  notNothing  =
    
Function (num?  As   Integer ) num IsNot  Nothing
Dim  arg  As   Integer   =   14
Console.WriteLine(
" Does the argument have an assigned value? " )
Console.WriteLine(notNothing(arg))
 

 

下面例子定义一个 lambda 表达式,返回数组最后一个元素的索引。

Dim  numbers()  =  { 0 1 2 3 4 5 6 7 8 9 }
Dim  lastIndex  =
    
Function (intArray()  As   Integer ) intArray.Length  -   1
For  i  =   0   To  lastIndex(numbers)
numbers(i) 
+=   1
Next

 

 

原文:http://msdn.microsoft.com/en-us/library/bb531253(v=VS.100).aspx

你可能感兴趣的:(express)