Soy un novato de C # y acabo de encontrar un problema. Existe una diferencia entre C # y Java cuando se trata del operador ternario ( ? :
).
En el siguiente segmento de código, ¿por qué no funciona la cuarta línea? El compilador muestra un mensaje de error de there is no implicit conversion between 'int' and 'string'
. La quinta línea no funciona tan bien. Ambos List
son objetos, ¿no?
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
Sin embargo, el mismo código funciona 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
¿Qué característica de lenguaje en C # falta? Si existe, ¿por qué no se agrega?
java
c#
ternary-operator
conditional-operator
blackr1234
fuente
fuente
int
no es un objeto. Es un primitivo.ArrayList<String>
y seArrayList<Integer>
convierte soloArrayList
en el código de bytes. Esto significa que parecen ser exactamente del mismo tipo en tiempo de ejecución. Aparentemente en C # son de diferentes tipos.Respuestas:
Mirando a través de la sección 7.14 de la Especificación del lenguaje C # 5: Operador condicional , podemos ver lo siguiente:
En otras palabras: trata de encontrar si xey se pueden convertir entre sí o no y, de no ser así, se produce un error de compilación. En nuestro caso
int
, ystring
no tienen la conversión explícita o implícita por lo que no se compilará.Compare esto con la sección 15.25 de la Especificación del lenguaje Java 7: Operador condicional :
Y, mirando la sección 15.12.2.7. Inferir argumentos de tipo basados en argumentos reales , podemos ver que intenta encontrar un ancestro común que sirva como el tipo utilizado para la llamada con la que aterriza
Object
.Object
es un argumento aceptable para que la llamada funcione.fuente
Las respuestas dadas son buenas; Les agregaría que esta regla de C # es una consecuencia de una guía de diseño más general. Cuando se le pide que infiera el tipo de expresión a partir de una de varias opciones, C # elige la mejor de ellas . Es decir, si le da a C # algunas opciones como "Jirafa, Mamífero, Animal", entonces podría elegir la más general - Animal - o podría elegir la más específica - Jirafa - dependiendo de las circunstancias. Pero debe elegir una de las opciones que realmente se le dieron . C # nunca dice "mis opciones son entre gato y perro, por lo tanto, deduciré que Animal es la mejor opción". Esa no fue una opción dada, por lo que C # no puede elegirla.
En el caso del operador ternario, C # intenta elegir el tipo más general de int y string, pero tampoco el tipo más general. En lugar de elegir un tipo que no era una opción en primer lugar, como object, C # decide que no se puede inferir ningún tipo.
También observo que esto está de acuerdo con otro principio de diseño de C #: si algo parece mal, dígaselo al desarrollador. El lenguaje no dice "Voy a adivinar lo que quisiste decir y seguir adelante si puedo". El lenguaje dice "Creo que has escrito algo confuso aquí, y te lo voy a contar".
Además, observo que C # no razona de la variable de hasta el valor asignado , sino más bien la otra dirección. C # no dice "estás asignando a una variable de objeto, por lo tanto, la expresión debe ser convertible en objeto, por lo tanto, me aseguraré de que lo sea". Más bien, C # dice que "esta expresión debe tener un tipo, y debo poder deducir que el tipo es compatible con el objeto". Dado que la expresión no tiene tipo, se produce un error.
fuente
int? b = (a != 0 ? a : (int?) null)
. También está esta pregunta , junto con todas las preguntas vinculadas al lado. Si continúa siguiendo los enlaces, hay bastantes. En comparación, nunca he oído que alguien se encuentre con un problema del mundo real con la forma en que Java lo hace.Respecto a la parte de genéricos:
En C #, el compilador intenta convertir las partes de expresión de la derecha a algún tipo común; dado que
List<int>
yList<string>
son dos tipos construidos distintos, uno no se puede convertir en el otro.En Java, el compilador intenta encontrar un supertipo común en lugar de convertir, por lo que la compilación del código implica el uso implícito de comodines y borrado de tipos ;
tiene el tipo de compilación de
ArrayList<?>
(en realidad, puede ser tambiénArrayList<? extends Serializable>
oArrayList<? extends Comparable<?>>
, dependiendo del contexto de uso, ya que ambos son supertipos genéricos comunes) y el tipo de tiempo de ejecución de rawArrayList
(ya que es el supertipo raw común).Por ejemplo (pruébelo usted mismo) ,
fuente
Write(two > six ? new List<object>() : new List<string>());
tampoco funciona.ArrayList<String>
yArrayList<Integer>
seríanArrayList
(el tipo sin formato), pero el tipo inferido para este operador ternario esArrayList<?>
(el tipo comodín). De manera más general, que el tiempo de ejecución de Java implemente genéricos mediante el borrado de tipos no tiene ningún efecto sobre el tipo de tiempo de compilación de una expresión.two > six ? new ArrayList<Integer>() : new ArrayList<String>()
es loArrayList<? extends Serializable&Comparable<?>>
que significa que puede asignarlo a una variable de tipoArrayList<? extends Serializable>
así como a una variable de tipoArrayList<? extends Comparable<?>>
, pero por supuestoArrayList<?>
, lo que es equivalente aArrayList<? extends Object>
, también funciona.Tanto en Java como en C # (y la mayoría de los otros lenguajes), el resultado de una expresión tiene un tipo. En el caso del operador ternario, existen dos posibles subexpresiones evaluadas para el resultado y ambas deben tener el mismo tipo. En el caso de Java, una
int
variable se puede convertir en unInteger
autoboxing. Ahora, dado que ambosInteger
yString
heredanObject
, se pueden convertir al mismo tipo mediante una simple conversión de restricción.Por otro lado, en C #, an
int
es una primitiva y no hay conversión implícita astring
ni ninguna otraobject
.fuente
int
aobject
.Integer/String
aObject
no es una conversión de reducción, es exactamente lo contrario :-)Integer
yString
también implementarSerializable
yComparable
así las asignaciones a cualquiera de ellos también funcionarán, por ejemplo,Comparable<?> c=condition? 6: "6";
oList<? extends Serializable> l = condition? new ArrayList<Integer>(): new ArrayList<String>();
son código Java legal.Esto es bastante sencillo. No hay conversión implícita entre string e int. el operador ternario necesita que los dos últimos operandos tengan el mismo tipo.
Tratar:
fuente