Иногда полезно взять вызов метода, дополненный параметрами, и превратить его в MethodInvoker, который вызовет указанную функцию с этими параметрами, без необходимости указывать параметры в данный момент. В других случаях полезно сделать что-то подобное, но оставить некоторые параметры открытыми. Этот тип действия называется "Карри". Каков наилучший шаблон для этого в VB?
Возможно использование лямбда-выражений в VB 2010, но лямбда-выражения несовместимы с редактированием и продолжением, и создаваемые ими замыкания могут иметь неожиданное поведение с помощью ссылки. Альтернативный подход заключается в определении некоторых общих методов, таких как показано здесь:
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 в качестве отдельного объекта кучи при создании делегата. Однако, если он будет упакован, использование структуры добавит накладные расходы на копирование структуры в упакованный экземпляр, не сохраняя ничего.