Je suis un débutant en C # et je viens de rencontrer un problème. Il y a une différence entre C # et Java lorsqu'il s'agit de l'opérateur ternaire ( ? :
).
Dans le segment de code suivant, pourquoi la 4ème ligne ne fonctionne-t-elle pas? Le compilateur affiche un message d'erreur de there is no implicit conversion between 'int' and 'string'
. La 5ème ligne ne fonctionne pas aussi bien. Les deux List
sont des objets, n'est-ce pas?
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
Cependant, le même code fonctionne en 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
Quelle fonctionnalité de langage en C # est manquante? Le cas échéant, pourquoi n'est-il pas ajouté?
java
c#
ternary-operator
conditional-operator
noirr1234
la source
la source
int
n'est pas un objet. C'est un primitif.ArrayList<String>
etArrayList<Integer>
devient justeArrayList
dans le bytecode. Cela signifie qu'ils semblent être exactement du même type au moment de l'exécution. Apparemment, en C #, ils sont de types différents.Réponses:
En parcourant la section 7.14 de la spécification du langage C # 5: Opérateur conditionnel, nous pouvons voir ce qui suit:
En d' autres termes: il essaie de trouver si oui ou non x et y peuvent être convertis en eachother et sinon, une erreur de compilation se produit. Dans notre cas
int
etstring
n'ont pas de conversion explicite ou implicite donc il ne se compilera pas.Comparez cela avec la section 15.25 de la spécification du langage Java 7: Opérateur conditionnel :
Et, en regardant la section 15.12.2.7. Inférence d'arguments de type basés sur des arguments réels, nous pouvons voir qu'il essaie de trouver un ancêtre commun qui servira de type utilisé pour l'appel avec lequel il aboutit
Object
.Object
est un argument acceptable pour que l'appel fonctionne.la source
Les réponses données sont bonnes; J'ajouterais que cette règle de C # est une conséquence d'une directive de conception plus générale. Lorsqu'on lui a demandé de déduire le type d'une expression à partir de l'un des nombreux choix, C # choisit le meilleur d'entre eux . Autrement dit, si vous donnez à C # des choix tels que "Girafe, Mammifère, Animal", il peut choisir le plus général - Animal - ou il peut choisir le plus spécifique - Girafe - selon les circonstances. Mais il doit choisir l' un des choix qui lui a été fait . C # ne dit jamais "mes choix sont entre le chat et le chien, donc je vais en déduire que l'animal est le meilleur choix". Ce n'était pas un choix donné, donc C # ne peut pas le choisir.
Dans le cas de l'opérateur ternaire, C # essaie de choisir le type le plus général d'int et de chaîne, mais le type le plus général n'est pas non plus. Plutôt que de choisir un type qui n'était pas un choix en premier lieu, comme object, C # décide qu'aucun type ne peut être déduit.
Je note également que cela est conforme à un autre principe de conception de C #: si quelque chose ne va pas, dites-le au développeur. Le langage ne dit pas "Je vais deviner ce que vous vouliez dire et me débrouiller si je peux". Le langage dit "Je pense que vous avez écrit quelque chose de déroutant ici, et je vais vous en parler."
De plus, je note que C # ne raisonne pas de la variable à la valeur assignée , mais plutôt dans l'autre sens. C # ne dit pas "vous assignez à une variable objet donc l'expression doit être convertible en objet, donc je vais m'assurer que c'est". Au contraire, C # dit "cette expression doit avoir un type, et je dois être capable de déduire que le type est compatible avec object". Comme l'expression n'a pas de type, une erreur est générée.
la source
int? b = (a != 0 ? a : (int?) null)
. Il y a aussi cette question , ainsi que toutes les questions liées sur le côté. Si vous continuez à suivre les liens, il y en a plusieurs. En comparaison, je n'ai jamais entendu parler de quelqu'un qui se heurte à un problème réel avec la façon dont Java le fait.Concernant la partie génériques:
En C #, le compilateur essaie de convertir les parties d'expression de droite en un type commun; puisque
List<int>
etList<string>
sont deux types construits distincts, l'un ne peut pas être converti en l'autre.En Java, le compilateur essaie de trouver un supertype commun au lieu de le convertir, donc la compilation du code implique l'utilisation implicite de caractères génériques et l' effacement de type ;
a le type de compilation de
ArrayList<?>
(en fait, cela peut être aussiArrayList<? extends Serializable>
ouArrayList<? extends Comparable<?>>
, selon le contexte d'utilisation, car ils sont tous les deux des supertypes génériques communs) et le type d'exécution de rawArrayList
(puisque c'est le supertype raw commun).Par exemple (testez-le vous-même) ,
la source
Write(two > six ? new List<object>() : new List<string>());
cela ne fonctionne pas non plus.ArrayList<String>
etArrayList<Integer>
seraientArrayList
(le type brut), mais le type déduit pour cet opérateur ternaire estArrayList<?>
(le type générique). Plus généralement, le fait que l'environnement d' exécution Java implémente des génériques via l'effacement de type n'a aucun effet sur le type au moment de la compilation d'une expression.two > six ? new ArrayList<Integer>() : new ArrayList<String>()
estArrayList<? extends Serializable&Comparable<?>>
ce qui signifie que vous pouvez l'affecter à une variable de typeArrayList<? extends Serializable>
ainsi qu'à une variable de typeArrayList<? extends Comparable<?>>
, mais bien sûrArrayList<?>
, ce qui équivaut àArrayList<? extends Object>
, fonctionne également.En Java et C # (et dans la plupart des autres langages), le résultat d'une expression a un type. Dans le cas de l'opérateur ternaire, il existe deux sous-expressions possibles évaluées pour le résultat et les deux doivent avoir le même type. Dans le cas de Java, une
int
variable peut être convertie en unInteger
par autoboxing. Maintenant, puisque les deuxInteger
etString
héritent deObject
, ils peuvent être convertis au même type par une simple conversion de rétrécissement.Par contre, en C #, an
int
est une primitive et il n'y a pas de conversion implicite enstring
ou autreobject
.la source
int
àobject
.Integer/String
enObject
n'est pas une conversion restrictive, c'est exactement le contraire :-)Integer
etString
aussi implémenterSerializable
etComparable
donc les affectations à l'un ou l'autre fonctionneront également, par exempleComparable<?> c=condition? 6: "6";
ouList<? extends Serializable> l = condition? new ArrayList<Integer>(): new ArrayList<String>();
sont du code Java légal.C'est assez simple. Il n'y a pas de conversion implicite entre string et int. l'opérateur ternaire a besoin que les deux derniers opérandes aient le même type.
Essayer:
la source