¿Por qué a menudo se ve "nulo! = Variable" en lugar de "variable! = Nulo" en C #?

103

En c #, ¿hay alguna diferencia en la velocidad de ejecución para el orden en el que indica la condición?

if (null != variable) ...
if (variable != null) ...

Desde hace poco veía el primero con bastante frecuencia y me llamó la atención ya que estaba acostumbrado al segundo.

Si no hay diferencia, ¿cuál es la ventaja del primero?

mr_georg
fuente
Algunos lenguajes de programación admiten la inicialización del valor en la condición if. En tales lenguajes, si queremos comparar LValue con RValue, es una buena práctica usar literal al lado izquierdo como if (1 == i). así que accidentalmente si ponemos = en lugar de == entonces da un error de compilación.
Jugal Panchal

Respuestas:

160

Es una retención de C. En C, si usa un compilador incorrecto o no tiene advertencias lo suficientemente altas, esto se compilará sin advertencia alguna (y de hecho es un código legal):

// Probably wrong
if (x = 5)

cuando probablemente quisiste decir

if (x == 5)

Puede solucionar esto en C haciendo:

if (5 == x)

Un error tipográfico aquí resultará en un código no válido.

Ahora, en C # todo esto es una tontería. A menos que esté comparando dos valores booleanos (que es raro, IME), puede escribir el código más legible, ya que una declaración "if" requiere una expresión booleana para comenzar, y el tipo de " x=5" Int32no lo es Boolean.

Sugiero que si ve esto en el código de sus colegas, los eduque en las formas de los lenguajes modernos y sugiera que escriban la forma más natural en el futuro.

Jon Skeet
fuente
6
Todos mis colegas me vuelven loco con esto, y quieren corchetes incluso para una sola declaración después del si ... (en caso de que agregue algo más tarde 'sin darme cuenta'). Ejecute F # y Boo (y YAML), si su idioma es ilegible sin sangría, hágalo parte del idioma, digo.
Benjol
48
Agregar llaves es razonable, en mi opinión: C # ciertamente no hace ningún intento por evitar que sea un problema. Eso es muy diferente al problema de la "constante == variable".
Jon Skeet
6
a if (esto! = eso) {performAsSuch (); } es bastante saludable en realidad. El argumento de la "adición posterior" me parece tan frágil como no evitar / escupir si (a = 5) errores tipográficos
jpinto3912
3
@jpinto: No estoy realmente seguro de lo que estás tratando de decir aquí - que haces como llaves alrededor de declaraciones individuales, o que no hacer? La diferencia entre esto y la variable de negocio en la pregunta es que en C # el código con un error tipográfico no es válido , por lo que el compilador lo detendrá. Lo mismo no es cierto para no poner frenos.
Jon Skeet
3
@Benjol No olvides proporcionar siempre una else { }cláusula vacía en caso de que alguien necesite agregar algo allí más tarde y se olvide de escribir la elsepalabra clave. (Broma)
Jeppe Stig Nielsen
12

Hay una buena razón para usar null primero: if(null == myDuck)

Si su class Duckanula el ==operador, entonces if(myDuck == null)puede entrar en un bucle infinito.

Usar nullprimero usa un comparador de igualdad predeterminado y realmente hace lo que pretendías.

(Escuché que eventualmente te acostumbras a leer código escrito de esa manera, simplemente no he experimentado esa transformación todavía).

Aquí hay un ejemplo:

public class myDuck
{
    public int quacks;
    static override bool operator ==(myDuck a, myDuck b)
    {
        // these will overflow the stack - because the a==null reenters this function from the top again
        if (a == null && b == null)
            return true;
        if (a == null || b == null)
            return false;

        // these wont loop
        if (null == a && null == b)
            return true;
        if (null == a || null == b)
            return false;
        return a.quacks == b.quacks; // this goes to the integer comparison
    }
}
DanW
fuente
11
Esto está mal. Voto en contra. Simplemente apunte el mouse sobre el ==operador y colóquelo allí, y verá que la sobrecarga definida por el usuario se usa en ambos casos. Por supuesto, puede hacer una diferencia sutil si verifica aantes by luego cambia la posición del aoperando derecho al operando izquierdo. Pero también podría verificar bantes a, y luego b == nullsería el que invirtió el orden. Todo esto es confuso para que el operador se llame a sí mismo. En su lugar, convierta uno de los operandos (o ambos) objectpara obtener la sobrecarga deseada. Me gusta if ((object)a == null)o if (a == (object)null).
Jeppe Stig Nielsen
10

Como todo el mundo ya notó, proviene más o menos del lenguaje C, donde podría obtener un código falso si accidentalmente olvida el segundo signo igual. Pero hay otra razón que también coincide con C #: Legibilidad.

Solo tome este simple ejemplo:

if(someVariableThatShouldBeChecked != null
   && anotherOne != null
   && justAnotherCheckThatIsNeededForTestingNullity != null
   && allTheseChecksAreReallyBoring != null
   && thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded != null)
{
    // ToDo: Everything is checked, do something...
}

Si simplemente intercambia todas las palabras nulas al principio, puede detectar mucho más fácilmente todas las verificaciones:

if(null != someVariableThatShouldBeChecked
   && null != anotherOne
   && null != justAnotherCheckThatIsNeededForTestingNullity
   && null != allTheseChecksAreReallyBoring
   && null != thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded)
{
    // ToDo: Everything is checked, do something...
}

Entonces, este ejemplo es quizás un mal ejemplo (consulte las pautas de codificación), pero solo piense en desplazarse rápidamente sobre un archivo de código completo. Simplemente viendo el patrón

if(null ...

inmediatamente sabes lo que viene a continuación.

Si fuera al revés, siempre debe escanear hasta el final de la línea para ver la verificación de nulidad, lo que le permitirá tropezar por un segundo para averiguar qué tipo de verificación se realiza allí. Entonces, tal vez el resaltado de sintaxis pueda ayudarlo, pero siempre es más lento cuando esas palabras clave están al final de la línea en lugar de al principio.

Oliver
fuente
9

Supongo que este es un programador en C que ha cambiado de idioma.

En C, puede escribir lo siguiente:

int i = 0;
if (i = 1)
{
    ...
}

Observe el uso de un solo signo igual allí, lo que significa que el código asignará 1 a la variable i, luego devolverá 1 (una asignación es una expresión) y usará 1 en la declaración if, que se manejará como verdadera. En otras palabras, lo anterior es un error.

Sin embargo, en C # esto no es posible. De hecho, no hay diferencia entre los dos.

Lasse V. Karlsen
fuente
1
"En C # sin embargo, esto no es posible" sin que el compilador se queje, ya que la instrucción if requiere una expresión booleana y la proporcionada es de tipo int.
Robert Harvey
4

En épocas anteriores, la gente olvidaba el '!' (o el '=' adicional para la igualdad, que es más difícil de detectar) y hacer una tarea en lugar de una comparación. poner el nulo al frente elimina la posibilidad del error, ya que nulo no es un valor l (es decir, no se le puede asignar).

La mayoría de los compiladores modernos dan una advertencia cuando haces una asignación en un condicional hoy en día, y C # en realidad da un error. La mayoría de la gente se queda con el esquema var == null ya que es más fácil de leer para algunas personas.

Rik
fuente
Todos los compiladores de C # (tenga en cuenta que esta es una pregunta de C #) deberían fallar al compilar una declaración "if" donde la expresión no es booleana ...
Jon Skeet
4

No veo ninguna ventaja en seguir esta convención. En C, donde no existen tipos booleanos, es útil escribir

if( 5 == variable)

más bien que

if (variable == 5)

porque si te olvidas de uno de los dos signos, terminas con

if (variable = 5)

que asigna 5 a la variable y siempre se evalúa como verdadero. Pero en Java, un booleano es un booleano. Y con! =, No hay ninguna razón.

Sin embargo, un buen consejo es escribir

if (CONSTANT.equals(myString))

más bien que

if (myString.equals(CONSTANT))

porque ayuda a evitar NullPointerExceptions.

Mi consejo sería pedir una justificación de la regla. Si no hay ninguno, ¿por qué seguirlo? No ayuda a la legibilidad

Gokkula Sudán R
fuente
0

Para mi siempre ha sido el estilo que prefieres

@Shy - Por otra parte, si confunde a los operadores, entonces debería querer obtener un error de compilación o estará ejecutando un código con un error, un error que regresa y lo muerde más adelante, ya que produjo un comportamiento inesperado

TheCodeJunkie
fuente
No creo que haya escuchado de nadie que prefiera la forma "constante == variable" que no sea ​​por razones de seguridad, que ya se tratan en C #.
Jon Skeet
Yo prefiero "variable == constante", pero he visto a programadores adoptar la inversa porque sienten que les resulta más natural.
TheCodeJunkie
1
¿Eran todas personas con años de lavado de cerebro C a sus espaldas, o fue una elección genuina?
Jon Skeet
0

Como muchos señalaron, está principalmente en el código C antiguo que se usó para identificar el error de compilación, ya que el compilador lo aceptó como legal

Los nuevos lenguajes de programación como java, go son lo suficientemente inteligentes como para capturar tales errores de compilación

No se deben usar condiciones similares a "nulo! = Variable" en el código, ya que es muy ilegible

Anand Shah
fuente
-4

Una cosa más ... Si está comparando una variable con una constante (entero o cadena, por ejemplo), poner la constante a la izquierda es una buena práctica porque nunca se encontrará con NullPointerExceptions:

int i;
if(i==1){        // Exception raised: i is not initialized. (C/C++)
  doThis();
}

mientras

int i;
if(1==i){        // OK, but the condition is not met.
  doThis();
}

Ahora, dado que por defecto C # instancia todas las variables, no debería tener ese problema en ese lenguaje.

karlipoppins
fuente
1
No estoy seguro de qué compilador está usando para el primer bit de código, pero debería compilarse (generalmente con una advertencia). El valor de i no está definido, pero tiene ALGUNO valor.
Dolphin
¡Por supuesto que ambos códigos se compilarán! Ese es exactamente el problema. Intente ejecutar el primer código y comprenderá lo que quiero decir con "Excepción planteada" ...
karlipoppins
Tampoco obtiene la diferencia entre los dos. ¿Puedes colaborar?
mfazekas
No hay excepción a menos que declare int * i en C / C ++, e incluso en ese caso comparará punteros y no arrojará ninguna excepción ... Como dijo Dolphin, el valor no está definido pero no generará ninguna excepción.
Cobusve
sin excepciones, digamos que ies un valor aleatorio sin una inicialización adecuada. la expresión tiene completamente la misma semántica en c / c ++
Raffaello