¿Por qué hay tanto un cortocircuito OR como una variación sin cortocircuito de ese operador en C #?

9

Periódicamente, me pregunto sobre esto:

¿El OR de cortocircuito siempre devolvería el mismo valor que el operador OR sin cortocircuito?

Espero que el OR de cortocircuito siempre se evalúe más rápidamente. Entonces, ¿se incluyó el operador OR sin cortocircuito en el lenguaje C # para mayor coherencia?

¿Qué me he perdido?

CarneyCode
fuente
99
Mismo valor de retorno: sí. Los mismos efectos secundarios: no.
Trabajo
2
Supongamos que f()plantea una excepción, considere true || f()y true | f(). ¿Ves la diferencia? La primera expresión se evalúa como true, la evaluación de la segunda da como resultado una excepción.
scarfridge

Respuestas:

25

Ambos operandos estaban destinados a cosas diferentes, provenientes de C que no tenía un tipo booleano. La versión de cortocircuito || solo funciona con booleanos, mientras que la versión sin cortocircuito | trabaja con tipos integrales, realizando un bit a bit o. Simplemente funcionó como una operación lógica sin cortocircuito para booleanos que están representados por un solo bit, siendo 0 o 1.

http://en.wikibooks.org/wiki/C_Sharp_Programming/Operators#Logical

Seguro
fuente
10
+1 para el enlace. Cuando leí esta pregunta pensé "¿qué?" dado que afaik el nombre oficial de estos es lógico y bit a bit o, y no cortocircuito y cortocircuito, que resultan ser efectos secundarios de su funcionamiento.
stijn
1
+1 Veo "opera solo en operandos booleanos" - Posiblemente no noté la diferencia porque solo uso expresiones que evalúan a booleano de todos modos
CarneyCode
2
Tuve que verificar, pero de hecho es cierto que el |operador, cuando se aplica a dos valores booleanos, se compila en el mismo oroperador en CIL que cuando se aplica a dos valores enteros, a diferencia de lo ||que se compila en CIL usando brtrueun condicional saltar.
quentin-starin
13

|(bitwise-or) no debe ser un cortocircuito para tipos como int. Eso es porque en casi todos los casos necesita calcular ambos lados de una |expresión para calcular el resultado correcto. Por ejemplo, ¿cuál es el resultado de 7 | f(x)?

Si f(x)no se evalúa, no se puede saber.

Además, sería incoherente hacer que este operador produzca un cortocircuito boolcuando no sea un cortocircuito int. Por cierto, creo que nunca lo he usado |para comparaciones lógicas intencionalmente, encontrar que es muy inconveniente hablar de |operador lógico.

|| sin embargo, es para comparaciones lógicas, donde la evaluación de cortocircuito funciona bien.

Lo mismo es válido incluso para C y C ++, donde está el origen de esos operadores.

Doc Brown
fuente
5

Esto es correcto, el operador OR de cortocircuito (||) siempre devolverá el mismo valor que el operador OR sin cortocircuito (|). (*)

Sin embargo, si el primer operando es verdadero, el operador de cortocircuito no causará la evaluación del segundo operando, mientras que el operador sin cortocircuito siempre causará la evaluación de ambos operandos. Esto puede tener un impacto en el rendimiento y, a veces, en los efectos secundarios.

Por lo tanto, hay un uso para ambos: si le importa el rendimiento y la evaluación del segundo operando no produce ningún efecto secundario (o si no le importan), entonces utilice el operador de cortocircuito. . Pero si por alguna razón necesita los efectos secundarios del segundo operando, entonces debe usar el operador sin cortocircuito.

Un ejemplo en el que debe usar el operador sin cortocircuito:

if( write_customer_to_database() != SUCCESS |
    write_supplier_to_database() != SUCCESS |
    write_order_to_database() != SUCCESS )
{
    transaction_rollback();
}

(*) Excepto en el caso de un escenario realmente pervertido en el que la evaluación del primer operando a falso causa por efecto secundario el segundo operando a evaluar como verdadero en lugar de falso.

Mike Nakis
fuente
2
+1 Pero me gustaría agregar eso además de usar funciones para indicar éxito o fracaso (como en su ejemplo): las funciones con efectos secundarios generalmente deben evitarse ...
Marjan Venema
1
Sí, probablemente por eso nunca he usado ninguno de los operadores que no sean de cortocircuito hasta el día de hoy, y las posibilidades son escasas de que alguna vez lo haga.
Mike Nakis
Ese ejemplo depende de los operandos que se evalúan en estricto orden de izquierda a derecha. C # garantiza esto, pero muchos lenguajes similares (incluidos C y C ++) no lo hacen.
Keith Thompson
¿No tendría sentido la evaluación de cortocircuito para ese ejemplo en particular, ya que todas las transacciones se revertirán si alguna de ellas falla? (Estoy haciendo algunas suposiciones sobre la semántica de las llamadas.)
Keith Thompson
@KeithThompson tiene razón, la evaluación sin cortocircuito causará un procesamiento innecesario y, en este sentido, el ejemplo es un poco lamentable, pero no es tan lamentable como para justificar el cambio, porque la verdad es que con la evaluación de cortocircuito si write_customer_to_database () tiene éxito, entonces el resto de las funciones write _... nunca serán llamadas, y es mejor trabajar correctamente en caso de éxito, que realizar algunas operaciones innecesarias en caso de falla.
Mike Nakis
4

Habría 2 razones para usar la variante de circuito no corto:

  1. mantener los efectos secundarios (pero es más claro codificar con una variable temporal)

  2. frustrar los ataques de tiempo evitando la bifurcación en el cortocircuito (esto incluso puede mejorar la eficiencia del tiempo de ejecución si el segundo operando es una variable pero eso es una microoptimización que el compilador puede hacer de todos modos)

monstruo de trinquete
fuente
Esta es la razón por la única razón siempre, no tengo ni idea de por qué esto no es la respuesta aceptada con mayor número de votos ...
Behrooz
2

si la cláusula de la derecha del operador tiene efectos secundarios, y el programador pretendía que deseara que ambos efectos secundarios ocurrieran antes de verificar su valor de retorno.

Lie Ryan
fuente
55
Eek No codifique así ... si confía en los efectos secundarios, tiene dos expresiones diferentes, asigna los resultados y los prueba después .
Konrad Rudolph