Wer entscheidet zuletzt, was der generische Typ ist?

7

Ich habe diese Funktion

%Vor%

Ich erstelle zwei Personen Klassen Instanzen:

%Vor%

Ich rufe die Funktion mit:

%Vor%

meine Frage ist:

Wer entscheidet eigentlich über den T1-Typ? (p? p2?)

Denn wenn der linke Apfel ist, prüft er, ob der zweite auch ein Apfel ist

und wenn der zweite Orange ist - er sollte überprüfen, ob der erste auch orange ist.

Es scheint seltsam, es zu fragen, weil es zur Kompilierzeit scheitern wird, wenn es nicht dasselbe ist.

Still - wer entscheidet über den Typ?

Und zweitens - Wenn ich es zu dynamisch ändere - zur Laufzeit - wer wird entscheiden, was der T1-Typ sein soll?

    
Royi Namir 05.02.2012, 12:54
quelle

6 Antworten

13

Auf einer hohen Ebene funktioniert Methodentypschlussfolgerung wie folgt.

Zuerst erstellen wir eine Liste aller Argumente - die von Ihnen angegebenen Ausdrücke - und deren entsprechenden formalen Parametertyp .

Sehen wir uns ein interessanteres Beispiel an als das, das Sie geben. Angenommen, wir haben

%Vor%

und der gleiche Anruf. Also machen wir die Korrespondenzen:

%Vor%

Dann machen wir eine Liste, was "Grenzen" für jeden Typparameter sind und ob sie "fest" sind. Wir haben zwei Typparameter und wir beginnen mit keinen oberen, unteren oder genauen Grenzen.

%Vor%

(Denken Sie an unsere jüngste Diskussion in einer anderen Frage über die relativen Größen von Typen, die darauf basieren, ob ein Typ mehr oder weniger restriktiv war oder nicht; ein restriktiverer Typ ist kleiner als ein solcher Typ weniger einschränkend: Giraffe ist kleiner als Animal, weil mehr Tiere Tiere sind als Giraffen Die "oberen" und "unteren" gebundenen Mengen sind genau das: Die Lösung des Typinferenzproblems für einen gegebenen Typparameter muss größer sein als oder identisch mit jeder unteren Grenze und kleiner als oder identisch mit jeder oberen Grenze und identisch mit jeder genauen Grenze.)

Dann betrachten wir jedes Argument und seinen entsprechenden Typ. (Wenn die Argumente lambdas sind, müssen wir vielleicht die Reihenfolge herausfinden, in der wir Argumente betrachten, aber Sie haben hier keine lambdas, also ignorieren wir dieses Detail.) Für jedes Argument, das wir machen eine Inferenz für den formalen Parametertyp und füge die Fakten hinzu, die wir über diese Ableitung zu der gebundenen Menge herleiten. Nach dem Betrachten des ersten Arguments folgern wir die Grenzen:

%Vor%

Nach dem zweiten Argument folgern wir die Grenzen

%Vor%

Nach dem dritten Argument folgern wir die Grenzen:

%Vor%

Nachdem wir so viel Fortschritte wie möglich gemacht haben, "reparieren" wir die Grenzen, indem wir den besten Typ in den Grenzen finden, der jedes gebundene erfüllt.

Für T1 gibt es zwei Arten in den Grenzen, Person und Employee . Gibt es eine davon, die jede Grenze in den Grenzen erfüllt? Ja. Employee erfüllt nicht die Person -Begrenzung, da Employee kleiner ist als Person ; Person ist eine untere Grenze - es bedeutet kein Typ kleiner als Person ist legal . Person erfüllt alle Grenzen: Person ist identisch mit Person und ist größer als Employee , also erfüllt es beide Grenzen. Der beste Typ im bounds-Set, der jede Grenze erfüllt, ist für T1 Person und für T2 offensichtlich int , da nur ein Typ in den Grenzen für T2 definiert ist. Also fixieren wir die Typparameter:

%Vor%

Dann fragen wir: "Haben wir eine feste Grenze für jeden Typparameter?" und die Antwort ist "Ja", so dass Inferenz erfolgreich ist.

  

Wenn ich den Typ des ersten Arguments in dynamic ändere, wie wird dann T1 abgeleitet?

Wenn ein Argument dynamisch ist, wird die Inferenz von T1 und T2 bis zur Laufzeit zurückgestellt. An diesem Punkt berücksichtigt der semantische Analysator den am weitesten abgeleiteten zugänglichen Laufzeittyp des Werts als den Typ für die bereitgestellte Untergrenze durch das dynamische Argument.

Wenn dieses Thema Sie interessiert und Sie mehr erfahren möchten, gibt es ein Video von mir, in dem Sie die C # 3-Version des Algorithmus erklären:

Ссылка

(C # 3 hatte keine oberen Schranken, nur untere und genaue Schranken; ansonsten sind die Algorithmen ziemlich gleich.)

Eine Reihe von Artikeln, die ich über Probleme mit Typinferenz geschrieben habe, sind hier:

Ссылка

    
Eric Lippert 05.02.2012, 17:20
quelle
7

Die Möglichkeit, die Typen im Aufruf zu überspringen

%Vor%

ist eine Syntax-Süßigkeit (außer Sie verwenden anonyme Typen) und kompiliert genau identisch zu

%Vor%

Der Compiler leitet die Werte für T1 und T2 entsprechend den Typen der Parameter a , b und c ab. Wenn p1 und p2 inkompatible Typen sind (siehe svicks Antwort ), kann der Compiler nicht ableiten T1 und dies führt zu einem Kompilierungsfehler.

    
Ilya Kogan 05.02.2012 12:58
quelle
2

Es gibt keine Priorität, beide (a und b) sollten gleich sein, das heißt, T1 wird beim Kompilieren aufgelöst. Wenn Sie zu dynamic wechseln, verschieben Sie einfach die Typauflösung in Runtime und sie wird dann bei compiletime fehlschlagen, wenn die Typen nicht identisch sind. Wenn Sie möchten, dass sie anders sind, müssen Sie T3 einführen.

Bearbeiten:

Der interessante Teil:

%Vor%

Ausgänge:

%Vor%

Aber das:

%Vor%

wird werfen:

%Vor%

Wie auch immer es scheint, dass ich wirklich ein gutes Buch oder eine Ressource über dynamische Typen in C # 4.0 finden muss, um die Magie zu verstehen, die hier passiert.

    
doblak 05.02.2012 12:59
quelle
1
  

Wer entscheidet eigentlich über den T1-Typ? (p? p2?)

Ist das nicht offensichtlich? Beide. Die Typen von p und p2 müssen kompatibel sein. Im Gegensatz zu dem, was andere Antworten sagen, müssen sie nicht gleich sein. Die eigentliche Regel ist, dass es eine implizite Umwandlung von einem der Typen in den anderen geben muss.

Also ist zum Beispiel MyFunc("a", new object(), 5) dasselbe wie MyFunc<object, int>("a", new object(), 5) , weil string implizit in object umgewandelt werden kann. Als weiteres Beispiel ist MyFunc(42L, 42, 4) dasselbe wie MyFunc<long, int>(42L, 42, 4) , weil int implizit in long konvertiert werden kann.

Es gibt auch Fälle, in denen die Fähigkeit, den Compiler auf die Typen schließen zu lassen, nicht nur nett ist, sondern auch notwendig. Dies geschieht insbesondere bei anonymen Typen. Zum Beispiel kann MyFunc(new { p = "p" }, new { p = "p2" }, 5) nicht neu geschrieben werden, um die Typen explizit anzugeben.

    
svick 05.02.2012 13:42
quelle
1

"Wer entscheidet eigentlich über den Typ T1? (p? p2?)"

Normalerweise entscheidet der C # -Compiler dies. Wenn eines der Methodenargumente dynamisch ist, wird die Entscheidung zur Laufzeit (von der Microsoft.CSharp-Bibliothek) getroffen. In beiden Fällen wird der in der C # -Spezifikation beschriebene Typinferenzalgorithmus angewendet:  Die beiden Typen p und p2 werden zu T1 's unteren Grenzen hinzugefügt ( obere Grenzen sind ebenfalls möglich, jedoch nur, wenn kontravariante Generika vorhanden sind beteiligt).

Dann wählt der Compiler einen der Typen in der Menge der Grenzen aus, der auch alle anderen Grenzen erfüllt. Wenn es nur eine Grenze gibt, weil p und p2 denselben Typ haben, ist diese Wahl trivial. Sonst (vorausgesetzt es sind nur untere Grenzen beteiligt) bedeutet dies, dass der Compiler einen Typ auswählt, so dass alle anderen möglichen Typen implizit in diesen Typ konvertierbar sind (was die Antwort von svick beschreibt).

Wenn es keine eindeutige solche Auswahl gibt, schlägt Typinferenz fehl - wenn möglich, wird eine andere Überladung ausgewählt, andernfalls tritt ein Compilerfehler auf (wenn die Entscheidung zur Laufzeit getroffen wird (dynamisch), wird stattdessen eine Ausnahme ausgelöst).

    
Daniel 05.02.2012 15:21
quelle
0

Wenn die Typen zur Kompilierungszeit explizit sind, prüft der Compiler die Typen der übergebenen Parameter und prüft, ob sie übereinstimmen und den Typen in den Generika zugeordnet werden können (keine Konflikte).

Wie auch immer, die eigentliche Überprüfung wird zu "Laufzeit" durchgeführt, der generische Code wird trotzdem als generisch kompiliert (anders als bei C ++ - Templates). Und dann, wenn der JIT-Compiler die Zeile kompiliert, wird es prüfen und sehen, ob es die Methode gemäß den Vorlagen erstellen kann, die Sie ihm gegeben haben, und die Parameter gesendet.

    
Yochai Timmer 05.02.2012 13:00
quelle

Tags und Links