¿Por qué> = devuelve falso cuando == devuelve verdadero para valores nulos?

78

¿Tengo dos variables de tipo int? (o Nullable <int> si lo desea). Quería hacer una comparación mayor o igual (> =) en las dos variables, pero resulta que esto devuelve falso si ambas variables son nulas, mientras que obviamente el operador == devuelve verdadero.

¿Puede alguien explicarme por qué eso es lógico porque la definición semántica del operador> = contiene la palabra "o"?

Koen
fuente
1
¿Puedes publicar el código que produce este comportamiento extraño?
Android Eve
12
En realidad, me preguntaría si es apropiado que == devuelva verdadero. No creo que sea apropiado en absoluto. ¿Cómo se pueden certificar como iguales dos variables cuyo valor se desconoce?
Charles Bretana
2
@Charles, porque, si son del mismo tipo anulado (por ejemplo int?), entonces se conoce su valor . null.
Moo-Juice
3
@ moo-juice, No en mi mundo ... Si tu nombre es nulo, ¿responderías a eso? nulo no es lo mismo que "nulo". Hay una vieja película llamada "Mi nombre es Nadie" ("¿Quién rompió el vidrio, niños?" ---- "Nadie") ¿Quién es "Nadie"? En el mundo real, (fuera de las partes internas del código), nulo significa que el valor es desconocido ... El valor de la estructura de datos puede ser conocido (lo es Null) pero la entidad / valor del dominio del problema real que la variable está ahí para representar es No conocido ..
Charles Bretana
2
@Charles, ¡me encanta la analogía !. ¿Pero quién no rompió el cristal?
Moo-Juice

Respuestas:

97

Hubo un gran debate sobre esta rareza cuando la función se diseñó originalmente en C # 2.0. El problema es que los usuarios de C # están completamente acostumbrados a que esto sea significativo:

if(someReference == null)

Al extender la igualdad a tipos de valor que aceptan valores NULL, tiene las siguientes opciones.

  1. La igualdad anulable se levanta realmente . Si uno o ambos operandos es nulo, el resultado no es ni verdadero ni falso, sino nulo. En este caso, puede:

    • a) Haga que sea ilegal tener una igualdad de tipo de valor anulable en una ifdeclaración, porque la ifdeclaración necesita un bool, no un bool anulable. En su lugar, solicite que todos usen HasValuesi quieren comparar con nulo. Esto es prolijo e irritante.

    • b) Convierta automáticamente nulo en falso. La desventaja de esto es que x==nulldevuelve falso si x es nulo, lo cual es confuso y va en contra de la comprensión de las personas sobre las comparaciones nulas con tipos de referencia.

  2. La igualdad anulable no se levanta. La igualdad que acepta valores NULL es verdadera o falsa, y la comparación con nulo es una comprobación nula. Esto hace que la igualdad anulable sea incompatible con la desigualdad anulable.

Ninguna de estas opciones es obviamente correcta; todos tienen pros y contras. VBScript elige 1b, por ejemplo. Después de mucho debate, el equipo de diseño de C # eligió el n. ° 2.

Eric Lippert
fuente
¿Cómo es inconsistente la igualdad que acepta valores NULL con la desigualdad que admite valores NULL en la opción n. ° 2?
MCS
3
@MCS: Exactamente de la manera que motiva la pregunta en primer lugar. == puede ser verdadero cuando <= es falso.
Eric Lippert
@Eric: Gracias, pensé que 'desigualdad' se refería solo a! = Que en realidad es consistente con ==. No me di cuenta de que es un término matemático: en.wikipedia.org/wiki/Inequality_(mathematics) .
MCS
1
Bueno, el otro tema (que no se ha abordado) es lo que hay que hacer cuando se intenta hacer <, <=, =>, o >cuando uno de los operandos es null. En C #, la respuesta es retorno false. En la Stringclase Scala / Java , la respuesta es lanzar un NullPointerException.
Ken Bloom
3
@Brian: entonces, ¿por qué permitir a los operadores en tipos que aceptan valores NULL? Si siempre arrojan el valor nulo del tipo que acepta valores NULL, entonces también puede definir el operador solo en los tipos que no aceptan valores NULL y hacer que el usuario inserte la conversión a no anulables, ya que eso es lo que van a tener que hacer para eliminar la excepción.
Eric Lippert
60

Porque la igualdad se define por separado de la comparabilidad.
Puede probar x == nullpero no x > nulltiene sentido. En C # siempre será falso.

Henk Holterman
fuente
1
+1: aquí está el enlace MSDN msdn.microsoft.com/en-us/library/2cf62fcy.aspx , pero desafortunadamente, se olvidaron de explicar el comportamiento de los operadores de comparación en caso de 2 nulos (mencionaron solo la igualdad) ...
digEmAll
3
sí, pero el operador es mayor o igual. Veo la tabla de verdad, pero tendería a estar de acuerdo con el OP,> = es mayor o igual, si nulo == nulo es verdadero, nulo> = nulo también debería ser cierto. Supongo que simplemente lo atribuimos a la implementación y la conveniencia del usuario para preservar los controles == nulos.
BlackICE
2
@David, vea la respuesta de Eric, "Ninguna de estas opciones es obviamente correcta". Pero en general, cuando un tipo es Equatable pero no Comparable, >=simplemente no está definido.
Henk Holterman
11

Otra forma de describir '> =' es: No menor que. Sin mención de iguales. Tan pronto como uno de los operandos en una prueba de no igualdad sea nulo, el resultado también será desconocido (es nulo). Sin embargo, si desea saber si ambos operandos son nulos, entonces Null == Null es una prueba razonable (debería dar como resultado verdadero). Deshacerse de la parte de desigualdad del operador marca la diferencia.

El siguiente ejemplo de código de http://msdn.microsoft.com/en-us/library/2cf62fcy.aspx#sectionToggle4 resume cómo C # trata Null:

int? num1 = 10;   
int? num2 = null;   
if (num1 >= num2)   
{   
    Console.WriteLine("num1 is greater than or equal to num2");   
}   
else   
{   
    // This clause is selected, but num1 is not less than num2.   
    Console.WriteLine("num1 >= num2 returned false (but num1 < num2 also is false)");   
}   

if (num1 < num2)   
{   
    Console.WriteLine("num1 is less than num2");   
}   
else   
{   
    // The else clause is selected again, but num1 is not greater than   
    // or equal to num2.   
    Console.WriteLine("num1 < num2 returned false (but num1 >= num2 also is false)");   
}   

if (num1 != num2)   
{   
    // This comparison is true, num1 and num2 are not equal.   
    Console.WriteLine("Finally, num1 != num2 returns true!");   
}   

// Change the value of num1, so that both num1 and num2 are null.   
num1 = null;   
if (num1 == num2)   
{   
    // The equality comparison returns true when both operands are null.   
    Console.WriteLine("num1 == num2 returns true when the value of each is null");   
}   

/* Output:   
 * num1 >= num2 returned false (but num1 < num2 also is false)   
 * num1 < num2 returned false (but num1 >= num2 also is false)   
 * Finally, num1 != num2 returns true!   
 * num1 == num2 returns true when the value of each is null   
 */   
NealB
fuente
Ese es un modelo mental interesante. Sin embargo, la Sección §1.4 de las especificaciones de C # llama a estos operadores Menor o igual y Mayor o igual que
Conrad Frix
3
@Conrad Que solo ilustra los problemas de traducir un lenguaje de programación (C # en este caso) al inglés. En mi humilde opinión, cada vez que los nulos aparecen en la lógica, debe lidiar con un resultado de tres estados (verdadero, falso, desconocido). Cualquier expresión que involucre Null debe resultar en desconocida con la única excepción de la Null == xcual es una prueba explícita para desconocido que resulta en verdadero o falso.
NealB
@NealB: En realidad, la especificación establece que> = y <= significan lo que esperarías que significaran; la sección §7.10 deja en claro que la operación 'x op y' para <= y> = está destinada a ser igual- a o mayor que / menos-que, como era de esperar.
nicodemus13
2

>=opera con un valor numérico; que nulo no es.

Podría sobrecargar al >=operador para que le proporcione lo que desea en un tipo específico.

Aaron McIver
fuente
opera en tipos nulos, devuelve falso
BlackICE
Maneja tipos nulos sí ... semántica sobre lo que vamos a definir como "opera". Codificación defensiva; ¿Es nulo entonces hacer x versus tratar nulo como un valor literal al tomar decisiones durante la evaluación?
Aaron McIver
Por lo general, no puede sobrecargar operadores porque solo puede definirlos dentro de sus propias clases. Entonces, en este caso, necesitaría acceder al Nullable<T>código de.
ANeves piensa que SE es malvado
0

NULL no es cero (valor numérico o binario), una cadena de longitud cero o un espacio en blanco (valor de carácter). Por lo tanto, cualquier operador de comparación siempre devolverá falso. Leer más sobre esto aquí

Sam
fuente
8
La base de datos NULL no es C # null. Además, los operadores de comparación en tipos que aceptan valores nulos de C # son una bestia extraña que no necesariamente siguen las reglas habituales para las comparaciones nulas.
Joren
3
La respuesta sigue siendo correcta, solo que el vínculo es incorrecto. msdn.microsoft.com/en-us/library/2cf62fcy.aspx#sectionToggle4
unholysampler
5
@unholy: La respuesta es incorrecta y, lo que es más importante, se basa en un razonamiento incorrecto.
Joren
0

¿Qué valores esperarías?

nulo == nulo verdadero

nulo> = nulo falso

nulo> nulo falso

nulo <= nulo falso

nulo <nulo falso

nulo! = nulo falso

1 == nulo falso

1> = nulo falso

1> nulo falso

1 <= nulo falso

1 <nulo falso

1! = Nulo verdadero también conocido como! (1 == nulo)

Bengie
fuente
0

> = solo significa "mayor o igual que" cuando se usa de esa manera específica bien definida. Cuando se usa en una clase con operadores sobrecargados, significa cualquier cosa que el desarrollador de la clase quiera que signifique. Cuando se aplica a una clase similar a una cadena, puede significar "ordena igual o superior" o puede significar "la misma longitud o más".

Sparr
fuente
0

Dado que, de forma predeterminada, an intno puede ser nulo y su valor se establecerá en 0, el operador de> y <, que está construido para inttipo, espera trabajar con valuesy no con nulls.

vea mi respuesta a una pregunta similar en la que escribí algunas formas de manejar nullable intcon los operadores less <y https://stackoverflow.com/a/51507612/7003760greater >

Mayer Spitzer
fuente