有时,进行方法调用、完成参数并将其转换为 MethodInvoker 会很有用,该方法将使用这些参数调用指定的函数,而无需当时指定参数。在其他时候,做一些类似的事情很有用,但要保留一些参数。这种类型的动作称为“Currying”。在 VB 中执行此操作的最佳模式是什么?
在 VB 2010 中可以使用 lambda 表达式,但 lambda 表达式与 edit-and-continue 不兼容,并且它们创建的闭包可能具有意外的引用行为。另一种方法是定义一些通用方法,如下所示:
Public Module CurryMagic
Delegate Sub Action(Of T1, T2)(ByVal P1 As T1, ByVal P2 As T2)
Delegate Sub Action(Of T1, T2, T3)(ByVal P1 As T1, ByVal P2 As T2, ByVal P3 As T3)
Class CurriedAction0(Of FixedType1, FixedType2)
Dim _theAction As Action(Of FixedType1, FixedType2)
Dim _FixedVal1 As FixedType1, _FixedVal2 As FixedType2
Sub Exec()
_theAction(_FixedVal1, _FixedVal2)
End Sub
Sub New(ByVal theAction As Action(Of FixedType1, FixedType2), _
ByVal FixedVal1 As FixedType1, ByVal FixedVal2 As FixedType2)
_theAction = theAction
_FixedVal1 = FixedVal1
_FixedVal2 = FixedVal2
End Sub
End Class
Class CurriedAction1(Of ArgType1, FixedType1, FixedType2)
Dim _theAction As Action(Of ArgType1, FixedType1, FixedType2)
Dim _FixedVal1 As FixedType1, _FixedVal2 As FixedType2
Sub Exec(ByVal ArgVal1 As ArgType1)
_theAction(ArgVal1, _FixedVal1, _FixedVal2)
End Sub
Sub New(ByVal theAction As Action(Of ArgType1, FixedType1, FixedType2), _
ByVal FixedVal1 As FixedType1, ByVal FixedVal2 As FixedType2)
_theAction = theAction
_FixedVal1 = FixedVal1
_FixedVal2 = FixedVal2
End Sub
End Class
Class ActionOf(Of ArgType1)
Shared Function Create(Of FixedType1, FixedType2)(ByVal theSub As Action(Of ArgType1, FixedType1, FixedType2), ByVal FixedVal1 As FixedType1, ByVal FixedVal2 As FixedType2) As Action(Of ArgType1)
Return AddressOf New CurriedAction1(Of ArgType1, FixedType1, FixedType2)(theSub, FixedVal1, FixedVal2).Exec
End Function
End Class
Function NewInvoker(Of FixedType1, FixedType2)(ByVal theSub As Action(Of FixedType1, FixedType2), ByVal FixedVal1 As FixedType1, ByVal FixedVal2 As FixedType2) As MethodInvoker
Return AddressOf New CurriedAction0(Of FixedType1, FixedType2)(theSub, FixedVal1, FixedVal2).Exec
End Function
End Module
如果我想创建一个 MethodInvoker 来执行 Foo(5, "Hello"),我可以使用
MyInvoker = NewInvoker(AddressOf Foo, 5, "Hello")
如果我想将 MyAction(X) 变成 Boz(X, "George", 9),其中 X 是 Double,我可以使用
MyAction = ActionOf(Of Double).Create(AddressOf Boz, "George", 9)
一切都非常漂亮,除了需要有大量的样板代码来容纳不同数量的固定和非固定参数,并且委托创建语法中没有任何内在的东西可以明确哪些参数是固定的,哪些是非固定的固定的。有没有办法改进模式?
附录:如果委托是从结构成员函数创建的,机制是什么?似乎委托获得了自己的结构副本,但我不知道该副本是装箱还是未装箱。如果没有装箱,用结构替换 CurryAction0 和 CurryAction1 将避免在创建委托时将 CurryAction0 或 CurryAction1 分配为单独的堆对象。但是,如果要装箱,则使用结构会增加将结构复制到装箱实例的开销,而不会保存任何内容。