Es scheint, dass die C # 3.0-Objektinitialisierersyntax es ermöglicht, das Klammerpaar open / close im Konstruktor auszuschließen, wenn ein parameterloser Konstruktor vorhanden ist. Beispiel:
var x = new XTypeName { PropA = value, PropB = value };
Im Gegensatz zu:
var x = new XTypeName() { PropA = value, PropB = value };
Ich bin gespannt, warum das Klammerpaar zum Öffnen / Schließen des Konstruktors hier nachher optional ist XTypeName
.
c#
syntax
types
language-design
initializer
James Dunne
quelle
quelle
Antworten:
Diese Frage war das Thema meines Blogs am 20. September 2010 . Die Antworten von Josh und Chad ("Sie bieten keinen Mehrwert, warum also sie benötigen?" Und "Redundanz beseitigen") sind grundsätzlich richtig. Um das ein bisschen mehr zu konkretisieren:
Die Funktion, mit der Sie die Argumentliste als Teil der "größeren Funktion" von Objektinitialisierern entfernen können, hat unsere Messlatte für "zuckerhaltige" Funktionen erfüllt. Einige Punkte, die wir berücksichtigt haben:
Schauen Sie sich diese Liste der Kriterien oben noch einmal an. Eine davon ist, dass die Änderung keine neue Mehrdeutigkeit in die lexikalische, grammatikalische oder semantische Analyse eines Programms einführt. Ihre vorgeschlagene Änderung führt zu einer Mehrdeutigkeit der semantischen Analyse:
Zeile 1 erstellt ein neues C, ruft den Standardkonstruktor auf und ruft dann die Instanzmethode M für das neue Objekt auf. Zeile 2 erstellt eine neue Instanz von BM und ruft den Standardkonstruktor auf. Wenn die Klammern in Zeile 1 optional wären, wäre Zeile 2 mehrdeutig. Wir müssten uns dann eine Regel ausdenken, um die Mehrdeutigkeit zu lösen. Wir konnten es nicht zu einem Fehler machen, da dies dann eine wichtige Änderung wäre, die ein vorhandenes legales C # -Programm in ein fehlerhaftes Programm umwandelt.
Daher müsste die Regel sehr kompliziert sein: Im Wesentlichen sind die Klammern nur dann optional, wenn sie keine Mehrdeutigkeiten verursachen. Wir müssten alle möglichen Fälle analysieren, die zu Mehrdeutigkeiten führen, und dann Code in den Compiler schreiben, um sie zu erkennen.
In diesem Licht gehen Sie zurück und schauen Sie sich alle Kosten an, die ich erwähne. Wie viele von ihnen werden jetzt groß? Komplizierte Regeln verursachen hohe Kosten für Design, Spezifikation, Entwicklung, Test und Dokumentation. Komplizierte Regeln verursachen in Zukunft viel häufiger Probleme mit unerwarteten Interaktionen mit Funktionen.
Alles für was? Ein winziger Kundennutzen, der der Sprache keine neue Repräsentationskraft verleiht, sondern verrückte Eckfälle hinzufügt, die nur darauf warten, eine arme, ahnungslose Seele, die darauf stößt, mit "Gotcha" anzuschreien. Solche Funktionen werden sofort gekürzt und in die Liste "Niemals tun" aufgenommen.
Dieser war sofort klar; Ich bin mit den Regeln in C # ziemlich vertraut, um festzustellen, wann ein gepunkteter Name erwartet wird.
Alle drei. Meistens schauen wir uns nur die Spezifikation und die Nudeln an, wie ich es oben getan habe. Angenommen, wir möchten C # einen neuen Präfixoperator namens "frob" hinzufügen:
(UPDATE:
frob
ist natürlichawait
; die Analyse hier ist im Wesentlichen die Analyse, die das Designteam beim Hinzufügen durchlaufen hatawait
.)"frob" ist hier wie "neu" oder "++" - es kommt vor einem Ausdruck irgendeiner Art. Wir erarbeiten die gewünschte Priorität und Assoziativität usw. und stellen dann Fragen wie "Was ist, wenn das Programm bereits einen Typ, ein Feld, eine Eigenschaft, ein Ereignis, eine Methode, eine Konstante oder einen lokalen Namen namens frob hat?" Das würde sofort zu Fällen führen wie:
Bedeutet das "Führen Sie die frob-Operation für das Ergebnis von x = 10 aus oder erstellen Sie eine Variable vom Typ frob mit dem Namen x und weisen Sie ihr 10 zu?" (Oder wenn frobbing eine Variable erzeugt, kann es sich um eine Zuweisung von 10 zu handeln
frob x
. Immerhin wird*x = 10;
analysiert und ist legal , wenn dies der Fallx
istint*
.)Bedeutet das "frob das Ergebnis des unären Plus-Operators auf x" oder "füge Ausdruck frob zu x hinzu"?
Und so weiter. Um diese Unklarheiten zu beseitigen, könnten wir Heuristiken einführen. Wenn Sie "var x = 10;" sagen das ist nicht eindeutig; es könnte bedeuten "den Typ von x ableiten" oder es könnte bedeuten "x ist vom Typ var". Wir haben also eine Heuristik: Wir versuchen zuerst, einen Typ namens var nachzuschlagen, und nur wenn einer nicht existiert, schließen wir auf den Typ von x.
Oder wir ändern die Syntax so, dass sie nicht mehrdeutig ist. Als sie C # 2.0 entwarfen, hatten sie dieses Problem:
Bedeutet das "Ausbeute x in einem Iterator" oder "Aufruf der Ertragsmethode mit Argument x"? Durch Ändern auf
es ist jetzt eindeutig.
Bei optionalen Parens in einem Objektinitialisierer ist es einfach zu überlegen, ob Mehrdeutigkeiten eingeführt wurden oder nicht, da die Anzahl der Situationen, in denen es zulässig ist, etwas einzuführen, das mit {beginnt, sehr gering ist . Grundsätzlich nur verschiedene Anweisungskontexte, Anweisungs-Lambdas, Array-Initialisierer und das war's auch schon. Es ist einfach, alle Fälle zu durchdenken und zu zeigen, dass es keine Unklarheiten gibt. Es ist etwas schwieriger sicherzustellen, dass die IDE effizient bleibt, kann aber ohne allzu große Probleme durchgeführt werden.
Diese Art des Herumspielens mit der Spezifikation ist normalerweise ausreichend. Wenn es eine besonders schwierige Funktion ist, ziehen wir schwerere Werkzeuge heraus. Beim Entwerfen von LINQ haben sich beispielsweise einer der Compiler-Mitarbeiter und einer der IDE-Mitarbeiter, die beide über einen Hintergrund in der Parser-Theorie verfügen, einen Parser-Generator erstellt, der Grammatiken auf Mehrdeutigkeiten analysieren und dann vorgeschlagene C # -Grammatiken für das Abfrageverständnis einspeisen kann ;; Dabei wurden viele Fälle festgestellt, in denen Abfragen nicht eindeutig waren.
Oder als wir in C # 3.0 eine erweiterte Typinferenz für Lambdas durchgeführt haben, haben wir unsere Vorschläge geschrieben und sie dann über den Teich an Microsoft Research in Cambridge gesendet, wo das dortige Sprachteam gut genug war, um einen formalen Beweis für den Typinferenzvorschlag auszuarbeiten theoretisch gesund.
Sicher.
In C # 1 ist klar, was das bedeutet. Es ist das gleiche wie:
Das heißt, es ruft G mit zwei Argumenten auf, die Bools sind. In C # 2 könnte dies bedeuten, was es in C # 1 bedeutete, aber es könnte auch bedeuten, "0 an die generische Methode F übergeben, die die Typparameter A und B verwendet, und dann das Ergebnis von F an G übergeben". Wir haben dem Parser eine komplizierte Heuristik hinzugefügt, die bestimmt, welchen der beiden Fälle Sie wahrscheinlich gemeint haben.
In ähnlicher Weise sind Casts auch in C # 1.0 nicht eindeutig:
Ist das "-x nach T umwandeln" oder "x von T subtrahieren"? Wieder haben wir eine Heuristik, die eine gute Vermutung macht.
quelle
Denn so wurde die Sprache angegeben. Sie bieten keinen Mehrwert. Warum sollten sie also einbezogen werden?
Es ist auch sehr ähnlich zu implizit typisierten Arrays
Referenz: http://msdn.microsoft.com/en-us/library/ms364047%28VS.80%29.aspx
quelle
Dies wurde getan, um die Konstruktion von Objekten zu vereinfachen. Die Sprachdesigner haben (meines Wissens) nicht ausdrücklich gesagt, warum sie dies für nützlich hielten, obwohl dies auf der Seite mit den C # Version 3.0-Spezifikationen ausdrücklich erwähnt wird :
Ich nehme an, dass sie der Meinung waren, dass die Klammer in diesem Fall nicht erforderlich war, um die Entwicklerabsicht anzuzeigen, da der Objektinitialisierer die Absicht anzeigt, stattdessen die Eigenschaften des Objekts zu konstruieren und festzulegen.
quelle
In Ihrem ersten Beispiel schließt der Compiler, dass Sie den Standardkonstruktor aufrufen (die C # 3.0-Sprachspezifikation besagt, dass der Standardkonstruktor aufgerufen wird, wenn keine Klammern angegeben sind).
Im zweiten Schritt rufen Sie explizit den Standardkonstruktor auf.
Sie können diese Syntax auch verwenden, um Eigenschaften festzulegen und Werte explizit an den Konstruktor zu übergeben. Wenn Sie die folgende Klassendefinition hatten:
Alle drei Aussagen sind gültig:
quelle
Ich bin kein Eric Lippert, daher kann ich nicht sicher sagen, aber ich würde annehmen, dass der Compiler die leere Klammer nicht benötigt, um auf das Initialisierungskonstrukt schließen zu können. Daher werden Informationen redundant und nicht benötigt.
quelle