Verschil tussen C# en Java’s ternaire operator (? 🙂

Ik ben een nieuweling in C# en ik loop tegen een probleem aan. Er is een verschil tussen C# en Java als het gaat om de ternaire operator (? :).

Waarom werkt de 4e regel niet in het volgende codesegment? De compiler toont een foutmelding van there is no implicit conversion between 'int' and 'string'. De 5e regel werkt niet zo goed. Beide Lists zijn objecten, nietwaar?

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

Dezelfde code werkt echter 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

Welke taalfunctie in C# ontbreekt? Indien van toepassing, waarom is het niet toegevoegd?


Antwoord 1, autoriteit 100%

Als we de C# 5-taalspecificatie sectie 7.14: Voorwaardelijke operatordoornemen, kunnen we het volgende zien:

  • Als x type X heeft en y type Y heeft, dan

    • Als er een impliciete conversie (§6.1) bestaat van X naar Y, maar niet van Y naar X, dan is Y het type van de
      voorwaardelijke uitdrukking.

    • Als er een impliciete conversie (§6.1) bestaat van Y naar X, maar niet van X naar Y, dan is X het type van de
      voorwaardelijke uitdrukking.

    • Anders kan er geen expressietype worden bepaald en treedt er een compileerfout op

Met andere woorden: het probeert te vinden of x en y wel of niet naar elkaarkunnen worden geconverteerd en zo niet, dan treedt er een compilatiefout op. In ons geval hebben inten stringgeen expliciete of impliciete conversie, dus het compileert niet.

Vergelijk dit met de Java 7 taalspecificatie sectie 15.25: Voorwaardelijke operator:

  • Als de tweede en derde operanden hetzelfde type hebben (wat het null-type kan zijn), dan is dat het type van de voorwaardelijke expressie. (NEE)
  • Als een van de tweede en derde operanden van het primitieve type T is, en het type van de andere het resultaat is van het toepassen van boksconversie (§5.1.7) op T, dan is het type van de voorwaardelijke uitdrukking T. ( NEE)
  • Als een van de tweede en derde operanden van het null-type is en het type van de andere een referentietype, dan is het type van de voorwaardelijke expressie dat referentietype. (NEE)
  • Anders, als de tweede en derde operanden typen hebben die converteerbaar zijn (§5.1.8) in numerieke typen, dan zijn er verschillende gevallen: (NEE)
  • Anders zijn de tweede en derde operanden respectievelijk van het type S1 en S2. Laat T1 het type zijn dat het resultaat is van het toepassen van boksconversie op S1 en laat T2 het type zijn dat het resultaat is van het toepassen van boksconversie op S2.
    Het type van de voorwaardelijke expressie is het resultaat van het toepassen van capture-conversie (§5.1.10) op lub(T1, T2) (§15.12.2.7). (JA)

En, kijkend naar sectie 15.12.2.7. Typeargumenten afleiden op basis van feitelijke argumentenwe kunnen zien dat het probeert een gemeenschappelijke voorouder te vinden die zal dienen als het type dat wordt gebruikt voor de aanroep die het met objectbelandt. objectiseen acceptabel argument, dus de aanroep zal werken.


Antwoord 2, autoriteit 81%

De gegeven antwoorden zijn goed; Ik zou hieraan willen toevoegen dat deze regel van C# een gevolg is van een meer algemene ontwerprichtlijn. Op de vraag om het type expressie af te leiden uit een van de verschillende keuzes, kiest C# de unieke beste ervan. Dat wil zeggen, als je C# een aantal keuzes geeft, zoals “Giraffe, Mammal, Animal”, dan kan het de meest algemene — Animal — of de meest specifieke — Giraffe — kiezen, afhankelijk van de omstandigheden. Maar het moet een van de keuzes kiezen die het daadwerkelijk heeft gekregen. C# zegt nooit “mijn keuzes zijn tussen kat en hond, daarom zal ik afleiden dat dier de beste keuze is”. Dat was geen gegeven keuze, dus C# kan het niet kiezen.

In het geval van de ternaire operator probeert C# het meer algemene type int en string te kiezen, maar geen van beide is het meer algemene type. In plaats van een type te kiezen dat in de eerste plaats geen keuze was, zoals object, besluit C# dat er geen type kan worden afgeleid.

Ik merk ook op dat dit in overeenstemming is met een ander ontwerpprincipe van C#: als iets er niet goed uitziet, vertel het dan aan de ontwikkelaar. De taal zegt niet: “Ik ga raden wat je bedoelde en doormodderen als ik kan”. De taal zegt: “Ik denk dat je hier iets verwarrends hebt geschreven, en daar ga ik je over vertellen.”

Ook merk ik op dat C# niet redeneert van de variabelenaar de toegewezen waarde, maar eerder in de andere richting. C# zegt niet “je wijst een objectvariabele toe, daarom moet de expressie converteerbaar zijn naar object, daarom zal ik ervoor zorgen dat dit het geval is”. In plaats daarvan zegt C# “deze expressie moet een type hebben en ik moet kunnen afleiden dat het type compatibel is met object”. Aangezien de uitdrukking geen type heeft, wordt er een fout geproduceerd.


Antwoord 3, autoriteit 22%

Met betrekking tot het generieke gedeelte:

two > six ? new List<int>() : new List<string>()

In C# probeert de compiler de rechtse uitdrukkingsdelen te converterennaar een algemeen type; aangezien List<int>en List<string>twee verschillende geconstrueerde typen zijn, kan de ene niet in de andere worden omgezet.

In Java probeert de compiler een algemeen supertype te vindenin plaats van te converteren, dus de compilatie van de code omvat het impliciete gebruik van wildcardsen verwijderen van typen ;

two > six ? new ArrayList<Integer>() : new ArrayList<String>()

heeft het compileertype ArrayList<?>(eigenlijk kan het ook ArrayList<? extends Serializable>of ArrayList<? extends Comparable<?>>, afhankelijk van de gebruikscontext, aangezien het beide veelvoorkomende generieke supertypes zijn) en runtime-type van onbewerkte ArrayList(omdat dit het algemene onbewerkte supertype is).

Bijvoorbeeld (test het zelf),

void test( List<?> list ) {
    System.out.println("foo");
}
void test( ArrayList<Integer> list ) { // note: can't use List<Integer> here
                                 // since both test() methods would clash after the erasure
    System.out.println("bar");
}
void test() {
    test( true ? new ArrayList<Object>() : new ArrayList<Object>() ); // foo
    test( true ? new ArrayList<Integer>() : new ArrayList<Object>() ); // foo 
    test( true ? new ArrayList<Integer>() : new ArrayList<Integer>() ); // bar
} // compiler automagically binds the correct generic QED

Antwoord 4, autoriteit 6%

In zowel Java als C# (en de meeste andere talen) heeft het resultaat van een expressie een type. In het geval van de ternaire operator zijn er twee mogelijke subexpressies geëvalueerd voor het resultaat en beide moeten hetzelfde type hebben. In het geval van Java kan een variabele intworden geconverteerd naar een Integerdoor autoboxing. Aangezien zowel Integerals stringerven van object, kunnen ze door een eenvoudige vernauwingconversie naar hetzelfde type worden geconverteerd.

Aan de andere kant, in C#, is een inteen primitief en is er geen impliciete conversie naar stringof een ander object.


Antwoord 5, autoriteit 5%

Dit is vrij eenvoudig. Er is geen impliciete conversie tussen string en int. de ternaire operator heeft de laatste twee operanden nodig om hetzelfde type te hebben.

Probeer:

Write(two > six ? two.ToString() : "6");

Other episodes