【51CTO精选译文】编写代码有许多方法。有时候,我们编写代码尽可能简单,如直接使用内联运算(inline calculations);有时我们却会给代码添加一些间接的层次。虽然这些间接的、抽象的层次使得我们在理解代码时更困难一些,但它却增加了程序的灵活性和动态性,这些特性对程序来说是非常实用而且有益的。
本文将向你介绍如何使用Lambda表达式来作为函数的参数,从而支持动态函数行为。如果你在程序的关键地方存在类似需求的话,那么使用这种方法你将创建一个更加灵活、功能强大的解决方案。
大多数情况
此时,大量的代码是用直接的方式进行编写。假设,你编写一个应用程序来计算营业税,直接写出计算过程是合理的。例如,美国密歇根州目前的销售税为6%,要计算营业税的话,你可以这样写:
- Dim total as Double = value * (1 + .06)
如果把这种计算写成内联形式的话,那问题很快就会解决掉,但它并不是一种灵活的、可重用的解决方案。下一步的改进方案是定义一个函数来执行计算,你可以为该函数提供可选参数。例如,你可以将计算表达式放到一个函数中去,使税款总额成为一个可选项,同时提供税款最常见的默认值。清单1显示的的是将计算过程转移到一个函数中的代码。
清单1:带有可选默认值和常规函数的可重用代码
- Function CalculateSalesTax(ByVal total As Double, _
- Optional ByVal tax As Double = 0.06) As Double
- Return total * (1 + tax)
- End Function
#T#所有能够编写出类似清单1中带有可选参数代码的初级程序员最终都会被聘用。
清单1中的解决方案是实用的,它能够满足很多情况的需求。这种解决方案唯一的缺点是它不灵活。您可以将金钱总额和税收数额一起传递,并且计算结果始终是相同的。此外,正如我之前所说的那样,对于定义函数解决方案而言,一个恰当命名的函数是可重用的,并且可以进行自我注释的。然而有些时候,你需要编写一个更加灵活的解决方案。
个别情况
有时,特别当你正在编写一些将被别人使用的代码时,您需要编写得更加灵活。你可以通过加入更多的函数参数来使你的代码更加灵活。函数参数的使用意味着该行为的一部分将以函数的形式进行传递。一般情况下,如果你不关心函数必须支持的所有情况,那么你可以使用这种编程方式。
您可以使用多种方法来定义函数参数。您可以定义一个委托(delegate)或者是定义一个函数——把参数类型作为委托类型的参数,也可以使用预先存在的泛型委托,例如Func。为了提供功能参数,您可以添加一个现有的第二函数和AddressOf运算符,您也可以传递一个Lambda表达式。清单2演示了如何定义一个使用了预定义Func泛型委托的函数参数,并且满足Lambda表达式的参数要求。
清单2:带有满足Lambda表达式参数的函数。
- Module Module1
- Sub Main()
- Dim total = CalculateSalesTax(100, 0.06, _
- Function(sale, tax) sale * (1 + tax))
- Console.WriteLine(total)
- Console.ReadLine()
- End Sub
- Function CalculateSalesTax(ByVal total As Double, _
- ByVal tax As Double, _
- ByVal calculator As Func(Of Double, Double, Double)) _
- As Double
- Return calculator(total, tax)
- End Function
- End Module
CalculateSalesTax把它所收到所有行为作为第三个参数。第三个参数定义为Func(Of Double, Double, Double),这是一个泛型委托。其他泛型委托分别为行动(T)和谓词(Of T)。
计算器参数可满足任何函数,它有两个double参数和double返回类型。这可能是一个函数地址,或是一个分配给相同类型变量(Func(Of Double, Double, Double))的Lambda表达式,也可能就是一个Lambda表达式。
清单2中的计算参数满足Lambda表达式
- Function(sale, tax) sale * (1 + tax)
Lambda表达式只是一个非常简单的函数。它使用函数的关键字,其参数名可以选择带或不带参数类型,以及一些执行工作的语句,其函数结尾、函数名称,以及关键字return均不是必需的。
编写类似代码使得消费者(使用这些代码的程序员)可以决定传递给函数的行为,比如说CalculateSalesTax 的例子。可以看下这种情况,营业税的税率是固定的,除非总金额高于5万元美元。虽然这些高价奢侈税在美国一些州中是强行征收的,但是消费者可以将奢侈税纳入另一个替代函数中。清单3显示了一个替代的代码(其中包括一个与奢侈税有关的任意值)。
清单3: 在清单2的基础上增加了额外功能的替代解决方案。
- Module Module1
- Sub Main()
- Dim calculator = Function(sale, tax) IIf(sale > 25000, _
- sale * (1 + tax + 0.02), sale * (1 + tax))
- Dim total = CalculateSalesTax(100000, 0.06, calculator)
- Console.WriteLine(total)
- Console.ReadLine()
- End Sub
- Function CalculateSalesTax(ByVal total As Double, _
- Optional ByVal tax As Double = 0.06) As Double
- Return total * (1 + tax)
- End Function
- Function CalculateSalesTax(ByVal total As Double, ByVal tax As Double, _
- ByVal calculator As Func(Of Double, Double, Double)) As Double
- Return calculator(total, tax)
- End Function
- End Module
请注意,在该解决方案中,一个新的Lambda表达式分配给了一个匿名变量。新的Lambda表达式使用IIf函数来判定销售金额是否超过了25,000元;额外花费的金额将被添加到征收奢侈税的列表中。
总结
该解决方案中的代码使用了一个Lambda表达式和一些函数参数。该解决方案可重复使用,非常灵活,因为定义函数参数意味着以后的消费者可以提供该解决方案中的一部分(参数部分)。
原文:Dynamic Programming with Lambda Expressions 作者:Paul Kimmel