Emulation von Delegaten mit freien generischen Typparametern in C #

8

Dies ist eine schwierige Frage zu Sprachdesign, Mustern und Semantik. Bitte stimmen Sie nicht ab, nur weil Sie den praktischen Wert nicht sehen.

Zuerst wollen wir über Funktionen und ihre Parameter nachdenken. Dann schauen wir uns die Analogien zwischen Funktionen mit ihren Parametern / Argumenten und generischen Klassen / Funktionen mit ihren Typ-Parametern / Typ-Argumenten an.

Funktionen sind Code-Blöcke mit einigen nicht spezifizierten Werten, die " Parameter " genannt werden. Sie liefern die Argumente und erhalten das Ergebnis.

Generische Klassen sind Klassen mit einigen nicht angegebenen " -Typ-Parametern ". Sie liefern die -Typ-Argumente und dann können Sie mit der Klasse arbeiten - rufen Sie den Konstruktor auf oder rufen Sie statische Methoden auf.

Generische Funktionen in nicht-generischen Klassen sind Funktionen mit einigen nicht spezifizierten " -Typ-Parametern " und einigen nicht spezifizierten " -Wertparametern ". Sie liefern die -Typ-Argumente und -Wert-Argumente , um das Ergebnis zu erhalten.

Delegierte sind Hinweise auf bestimmte Funktionen. Wenn Sie einen Delegaten erstellen, geben Sie die Funktion Argumente nicht an, sondern liefern Sie später.

Das Problem ist, dass .Net nicht das Äquivalent von Delegates für generische Funktionen mit nicht spezifizierten generischen type-parameters hat. Sie können type-values ​​ später nicht für die type-Parameter angeben. Wir können uns Delegierte vorstellen, die nicht nur freie Wertparameter , sondern auch freie Typparameter haben.

%Vor%

Unten ist der [Pseudo-] Code für ein Programm, das ich in C # schreiben möchte. Ich möchte wissen, wie man das gleiche Verhalten in einem korrekten C # -Programm erreichen kann.

Wie können wir Delegierte mit freien generischen Typparametern in C # emulieren?

Wie können wir die Referenz / den Link zu generischen Funktionen [s] mit noch unbekannten generischen Parametern durch den nicht-generischen Code übergeben?

%Vor%

Sehen Sie, wie wir die Referenzen auf generische Funktionen (Factory.CreateList und Factory.CreateSet) an den Worker-Klassenkonstruktor übergeben, ohne die -Typargumente anzugeben ? Type-Argumente werden später bereitgestellt, wenn die generische DoWork-Funktion mit konkret typisierten Arrays aufgerufen wird. DoWork verwendet die type-arguments , um die richtige Funktion auszuwählen, übergibt value-arguments an sie und gibt den empfangenen Wert zurück.

Endgültige Lösung: Emulation von Delegaten mit freien generischen Typparametern in C #

    
Ark-kun 14.10.2012, 02:42
quelle

3 Antworten

7

Ich denke, Sie emulieren dies in der Sprache, indem Sie keine Delegaten, sondern Interfaces verwenden. Eine nicht generische Schnittstelle kann eine generische Methode enthalten, so dass Sie das meiste Verhalten von Delegaten mit offenen Argumenten erhalten können.

Hier ist Ihr Beispiel, das in ein gültiges C # -Programm überarbeitet wurde (Beachten Sie, dass es immer noch die von Ihnen definierte Factory-Klasse benötigt):

%Vor%

Sie erhalten immer noch die wichtige Funktion, die Entscheidung darüber, welche Methode aufgerufen werden soll, von der Ausführung dieser Methode zu trennen.

Eine Sache, die Sie jedoch nicht tun können, ist das Speichern eines beliebigen Zustands in den IWorker -Implementierungen auf generische Weise. Ich bin mir nicht sicher, wie das nützlich sein könnte, weil die Methode DoWork jedes Mal mit anderen Typargumenten aufgerufen werden könnte.

    
mike z 14.10.2012, 03:40
quelle
2

Dies ist unter dem Typensystem von .Net eigentlich nicht sinnvoll.

Was Sie beschreiben, ist ein -Typkonstruktor - eine "Funktion", die einen oder mehrere Typen akzeptiert und einen konkreten ( parametrisierten oder geschlossenen <) zurückgibt / em>) eingeben.

Das Problem ist, dass Typkonstruktoren selbst keine Typen sind. Sie können kein Objekt oder eine Variable eines offenen Typs haben. Typkonstruktoren können nur verwendet werden, um konkrete Typen zu generieren.

Mit anderen Worten, es gibt keine Möglichkeit, einen Verweis auf eine offene Funktion innerhalb des Typsystems von .Net darzustellen.

Das Beste, was Sie tun können, ist die Reflexion; a MethodInfo kann eine offene generische Methode beschreiben.
Sie können eine typsichere Referenz zur Kompilierungszeit auf ein offenes MethodInfo erhalten, indem Sie eine generische Methode schreiben, die eine Ausdrucksbaumstruktur mit einem falschen generischen Parameter verwendet:

%Vor%

Der Parameter TPlaceholder ist erforderlich, wenn Sie eine offene generische Methode mit einer Einschränkung für diesen Parameter referenzieren möchten. Sie können einen Platzhaltertyp auswählen, der die Bedingung erfüllt.

    
SLaks 14.10.2012 02:53
quelle
2

Die Lösung sind Schnittstellen. Wie @ mike-z schrieb, unterstützen Schnittstellen generische Methoden. So können wir die nicht-generische Schnittstelle IFactory mit einer generischen Methode erstellen, die einen Verweis auf eine generische Methode in einer Klasse enthält. Um die generische Methode einer [Factory] -Klasse unter Verwendung einer solchen Schnittstelle zu binden, müssen normalerweise kleine Klassen erstellt werden, die die IFactory-Schnittstelle implementieren. Sie verhalten sich wie Verschlüsse, die von Lambdas verwendet werden.

Ich sehe keinen großen semantischen Unterschied zwischen diesen und den generischen Methodendelegierten, nach denen ich gefragt habe. Die Lösung ähnelt sehr dem, was der Compiler für lambdas [die nur andere Methoden aufrufen] verwendet (Schließen mit der Methode, die die Methode aufruft).

Was verlieren wir? Hauptsächlich syntaktischer Zucker.

  • Anonyme Funktionen / Lambdas. Wir können keine generischen Lambdas erstellen. Sein in der Lage, anonyme Klassen (wie in Java) zu erstellen, hätte das gelöst Problem. Aber das ist zunächst kein großes Problem als Lambdas sind nur syntaktischer Zucker in .Net.

  • Möglichkeit, Delegat / Link implizit aus der Methodengruppe zu erstellen (C # Begriff). Wir können die Methodengruppe in keiner Weise verwenden, wenn sie generisch ist. Dies wirkt sich auch nicht auf die Semantik aus.

  • Die Fähigkeit, generische Delegaten zu definieren, ist behindert. Wir können nicht ein generische IFactory<U, V> Schnittstelle mit der Methode V<T> Create<T>(U<T> arg) . Das ist auch kein Problem.

Dies ist der Code der Lösung. Die Factory -Klasse aus der Frage ist unverändert.

%Vor%     
Ark-kun 17.10.2012 04:09
quelle