A veces es útil tomar una llamada al método, completar con parámetros y convertirlo en un MethodInvoker que invocará la función indicada con esos parámetros, sin tener que especificar los parámetros en ese momento. En otras ocasiones, es útil hacer algo similar, pero dejando algunos parámetros abiertos. Este tipo de acción se llama "Currying". ¿Cuál es el mejor patrón para hacer esto en VB?¿Cuál es el mejor patrón para curry para delegar parámetros (usando .NET 2.0 o posterior)?
Es posible utilizar expresiones lambda en VB 2010, pero las expresiones lambda no son compatibles con edit-and-continue, y los cierres que crean pueden tener comportamientos de referencia no esperados. Un enfoque alternativo es definir algunos métodos genéricos, como se muestra aquí:
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
Si quiero crear un MethodInvoker que llevará a cabo Foo (5, "Hola"), que puede crear una con
MyInvoker = NewInvoker(AddressOf Foo, 5, "Hello")
y si quiero convertir MyAction (X) en Boz (X, "George", 9), donde X es un doble, que puede utilizar
MyAction = ActionOf(Of Double).Create(AddressOf Boz, "George", 9)
Todo bastante resbaladiza, excepto que es necesario tener una gran cantidad de código repetitivo para acomodar diferentes números de parámetros fijos y no fijos, y no hay nada inherente en la sintaxis de creación de delegados que aclare qué parámetros son fijos y cuáles no son fijos. ¿Hay alguna manera de mejorar el patrón?
Addendum: ¿Cuál es el mecanismo si se crea un delegado a partir de una función miembro de estructura? Parece que el delegado obtiene su propia copia de la estructura, pero no sé si esa copia está enmarcada o no. Si no está encuadrado, reemplazar CurryAction0 y CurryAction1 con estructuras evitaría la necesidad de asignar un CurryAction0 o CurryAction1 como un objeto de montón separado al crear el delegado. Sin embargo, si se va a encasillar, el uso de una estructura agregaría la sobrecarga de copiar la estructura a la instancia en caja sin guardar nada.
No estoy seguro de lo que quiere, pero aprovecho la oportunidad: puede crear delegados sin especificar parámetros y luego pasarlos como Object-Array a través del método Invoke. – Bobby
* 'Este tipo de acción se llama "Currying' '' - en realidad es una aplicación parcial. 'Currying' se refiere a la forma en que ciertos lenguajes estructuran las funciones para que sean fáciles de aplicar parcialmente. http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application –
@Tim Robinson: Hmm ... con esa descripción, currying sería el proceso de crear el método que realiza la aplicación parcial, a diferencia del acto de realizar la aplicación en sí? En cualquier caso, he estado buscando una buena manera de hacerlo. Mi enfoque requiere mucho código repetitivo para funcionar, lo cual es molesto, pero evita las molestias de los cierres. Por cierto, ¿los delegados están en las estructuras en caja o sin caja? Si no está en la casilla, mi patrón podría mejorarse mediante el uso de estructuras en lugar de clases, pero si está en caja eso sería un desperdicio. – supercat