Ich bin ein C # -Neuling und habe gerade ein Problem. Beim Umgang mit dem ternären Operator ( ? :
) gibt es einen Unterschied zwischen C # und Java .
Warum funktioniert die 4. Zeile im folgenden Codesegment nicht? Der Compiler zeigt eine Fehlermeldung von an there is no implicit conversion between 'int' and 'string'
. Die 5. Zeile funktioniert nicht so gut. Beide List
sind Objekte, nicht wahr?
int two = 2;
double six = 6.0;
Write(two > six ? two : six); //param: double
Write(two > six ? two : "6"); //param: not object
Write(two > six ? new List<int>() : new List<string>()); //param: not object
Der gleiche Code funktioniert jedoch in Java:
int two = 2;
double six = 6.0;
System.out.println(two > six ? two : six); //param: double
System.out.println(two > six ? two : "6"); //param: Object
System.out.println(two > six ? new ArrayList<Integer>()
: new ArrayList<String>()); //param: Object
Welche Sprachfunktion in C # fehlt? Wenn überhaupt, warum wird es nicht hinzugefügt?
java
c#
ternary-operator
conditional-operator
blackr1234
quelle
quelle
int
kein Objekt ist. Es ist ein Primitiv.ArrayList<String>
undArrayList<Integer>
wird nurArrayList
im Bytecode. Dies bedeutet, dass sie zur Laufzeit genau vom gleichen Typ zu sein scheinen . Anscheinend sind sie in C # verschiedene Typen.Antworten:
Wenn Sie sich den Abschnitt 7.14 der C # 5-Sprachspezifikation ansehen : Bedingter Operator , sehen Sie Folgendes:
Mit anderen Worten: Es wird versucht herauszufinden, ob x und y ineinander konvertiert werden können. Andernfalls tritt ein Kompilierungsfehler auf. In unserem Fall
int
undstring
haben keine explizite oder implizite Konvertierung, so dass es nicht kompiliert wird.Vergleichen Sie dies mit der Java 7-Sprachspezifikation in Abschnitt 15.25: Bedingter Operator :
Und siehe Abschnitt 15.12.2.7. Ableiten von Typargumenten Basierend auf tatsächlichen Argumenten können wir sehen, dass versucht wird, einen gemeinsamen Vorfahren zu finden, der als Typ für den Aufruf dient, mit dem er landet
Object
.Object
ist ein akzeptables Argument, damit der Aufruf funktioniert.quelle
Die gegebenen Antworten sind gut; Ich möchte ihnen hinzufügen, dass diese Regel von C # eine Folge einer allgemeineren Konstruktionsrichtlinie ist. Wenn C # aufgefordert wird, den Typ eines Ausdrucks aus einer von mehreren Auswahlmöglichkeiten abzuleiten, wählt er das eindeutig beste aus . Das heißt, wenn Sie C # einige Optionen wie "Giraffe, Säugetier, Tier" geben, wählt es je nach den Umständen die allgemeinste - Tier - oder die spezifischste - Giraffe. Aber es muss eine der Entscheidungen treffen, die es tatsächlich gegeben hat . C # sagt nie "Ich habe die Wahl zwischen Katze und Hund, daher werde ich daraus schließen, dass Tier die beste Wahl ist". Das war keine Wahl, also kann C # sie nicht wählen.
Im Fall des ternären Operators versucht C #, den allgemeineren Typ von int und string zu wählen, aber auch nicht den allgemeineren Typ. Anstatt einen Typ auszuwählen, der überhaupt keine Wahl war, wie z. B. ein Objekt, entscheidet C #, dass kein Typ abgeleitet werden kann.
Ich stelle auch fest, dass dies im Einklang mit einem anderen Designprinzip von C # steht: Wenn etwas falsch aussieht, informieren Sie den Entwickler. Die Sprache sagt nicht "Ich werde raten, was du gemeint hast und durchwühlen, wenn ich kann". Die Sprache sagt: "Ich denke, Sie haben hier etwas Verwirrendes geschrieben, und ich werde Ihnen davon erzählen."
Außerdem stelle ich fest, dass C # nicht aus dem Variablen zum zugewiesenen Wert führt , sondern in die andere Richtung. C # sagt nicht "Sie weisen einer Objektvariablen zu, daher muss der Ausdruck in ein Objekt konvertierbar sein, daher werde ich sicherstellen, dass dies der Fall ist". Vielmehr sagt C #: "Dieser Ausdruck muss einen Typ haben, und ich muss ableiten können, dass der Typ mit dem Objekt kompatibel ist." Da der Ausdruck keinen Typ hat, wird ein Fehler erzeugt.
quelle
int? b = (a != 0 ? a : (int?) null)
. Es gibt auch diese Frage , zusammen mit allen verknüpften Fragen auf der Seite. Wenn Sie den Links weiter folgen, gibt es einige. Im Vergleich dazu habe ich noch nie von jemandem gehört, der auf ein reales Problem mit Java stößt.In Bezug auf den Generika-Teil:
In C # versucht der Compiler , die rechten Ausdrucksteile in einen allgemeinen Typ zu konvertieren . Da
List<int>
undList<string>
zwei verschiedene konstruierte Typen sind, kann einer nicht in den anderen konvertiert werden.In Java versucht der Compiler , einen gemeinsamen Supertyp zu finden, anstatt ihn zu konvertieren. Daher umfasst die Kompilierung des Codes die implizite Verwendung von Platzhaltern und das Löschen von Typen .
hat den Kompilierungstyp
ArrayList<?>
(tatsächlich kann es auch seinArrayList<? extends Serializable>
oderArrayList<? extends Comparable<?>>
, abhängig vom Verwendungskontext, da beide gängige generische Supertypen sind) und den Laufzeittyp von rawArrayList
(da es sich um den gemeinsamen rohen Supertyp handelt).Zum Beispiel (testen Sie es selbst) ,
quelle
Write(two > six ? new List<object>() : new List<string>());
auch nicht.ArrayList<String>
undArrayList<Integer>
wärenArrayList
(der Rohtyp), aber der abgeleitete Typ für diesen ternären Operator istArrayList<?>
(der Platzhaltertyp). Allgemeiner gesagt hat die Implementierung von Generika durch Java- Laufzeit durch Löschen des Typs keine Auswirkung auf den Typ der Kompilierungszeit eines Ausdrucks.two > six ? new ArrayList<Integer>() : new ArrayList<String>()
heißt ,ArrayList<? extends Serializable&Comparable<?>>
das bedeutet , dass Sie es auf eine Variable vom Typ zuweisen können ,ArrayList<? extends Serializable>
sowie eine Variable vom TypArrayList<? extends Comparable<?>>
, aber natürlichArrayList<?>
, was äquivalent istArrayList<? extends Object>
, funktioniert auch.Sowohl in Java als auch in C # (und den meisten anderen Sprachen) hat das Ergebnis eines Ausdrucks einen Typ. Im Fall des ternären Operators werden zwei mögliche Unterausdrücke für das Ergebnis ausgewertet, und beide müssen denselben Typ haben. Im Fall von Java kann eine
int
VariableInteger
durch Autoboxing in eine Variable konvertiert werden. Da nun beideInteger
und vonString
erbenObject
, können sie durch eine einfache Verengungskonvertierung in denselben Typ konvertiert werden.Andererseits ist an in C #
int
ein Primitiv und es gibt keine implizite Konvertierung zustring
oder einem anderenobject
.quelle
int
nachobject
.Integer/String
nachObject
ist keine verengende Konvertierung, es ist genau das Gegenteil :-)Integer
undString
auch implementierenSerializable
undComparable
so funktionieren Zuweisungen zu einem von ihnen auch, z. B.Comparable<?> c=condition? 6: "6";
oderList<? extends Serializable> l = condition? new ArrayList<Integer>(): new ArrayList<String>();
sind legaler Java-Code.Das ist ziemlich einfach. Es gibt keine implizite Konvertierung zwischen string und int. Der ternäre Operator benötigt die letzten beiden Operanden, um denselben Typ zu haben.
Versuchen:
quelle