Manchmal ist es sinnvoll, einen Methodenaufruf mit Parametern in einen MethodInvoker zu verwandeln, der die angegebene Funktion mit diesen Parametern aufruft, ohne dass die Parameter zu diesem Zeitpunkt angegeben werden müssen. In anderen Fällen ist es nützlich, etwas Ähnliches zu tun, aber einige Parameter offen zu lassen. Diese Art von Aktion wird "Currying" genannt. Was ist das beste Muster, um dies in VB zu tun?
Es ist möglich, Lambda-Ausdrücke in VB 2010 zu verwenden, aber Lambda-Ausdrücke sind nicht kompatibel mit edit-and-continue, und die Schließungen, die sie erstellen, können unerwartete By-Referenz-Verhalten haben. Ein alternativer Ansatz besteht darin, einige generische Methoden zu definieren, wie hier gezeigt:
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
Wenn ich einen MethodInvoker erstellen möchte, der Foo(5, "Hello") ausführt, kann ich einen erstellen, indem ich
MyInvoker = NewInvoker(AddressOf Foo, 5, "Hello")
und wenn ich MyAction(X) in Boz(X, "George", 9) umwandeln möchte, wobei X ein Double ist, kann ich Folgendes verwenden
MyAction = ActionOf(Of Double).Create(AddressOf Boz, "George", 9)
Alles ziemlich raffiniert, außer dass es notwendig ist, eine riesige Menge an Boilerplate-Code zu haben, um eine unterschiedliche Anzahl von festen und nicht festen Parametern unterzubringen, und es gibt nichts inhärent in der Syntax der Delegatenerstellung, die deutlich macht, welche Parameter fest und welche nicht fest sind. Gibt es eine Möglichkeit, das Muster zu verbessern?
Anexo : Was ist der Mechanismus, wenn ein Delegierter aus einer struct-Mitgliedsfunktion erstellt wird? Es scheint, dass der Delegat seine eigene Kopie der Struktur erhält, aber ich weiß nicht, ob diese Kopie gepackt oder nicht gepackt ist. Wenn es nicht boxed ist, würde das Ersetzen von CurryAction0 und CurryAction1 mit structs die Notwendigkeit zu vermeiden, eine CurryAction0 oder CurryAction1 als ein separates Heap-Objekt zuzuweisen, wenn der Delegat erstellt wird. Wenn es geht zu sein, boxed, obwohl, mit einem struct würde der Overhead des Kopierens der struct auf die boxed Instanz hinzufügen, während nichts speichern.