11 Stimmen

Was ist das beste Muster, um Delegate-Parameter (mit .NET 2.0 oder höher) zu kurieren?

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.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X