logical - diferente en c#
Cortocircuito en los operadores de asignaciĆ³n |=y &=en C# (4)
Sé que ||
y &&
se definen como operadores de cortocircuito en C #, y tal comportamiento está garantizado por la especificación del lenguaje, pero ¿ |=
y &=
cortocircuito también?
Por ejemplo:
private bool IsEven(int n)
{
return n % 2 == 0;
}
private void Main()
{
var numbers = new int[] { 2, 4, 6, 8, 9, 10, 14, 16, 17, 18, 20 };
bool allEven = true;
bool anyOdd = false;
for (int i = 0; i < numbers.Length; i++)
{
allEven &= IsEven(numbers[i]);
anyOdd |= !IsEven(numbers[i]);
}
}
Cuando se golpea la entrada 9, allEven
vuelve falso, lo que significa que todas las entradas subsiguientes son irrelevantes: se garantiza que el valor de allEven
es falso para todas las llamadas futuras a esa expresión. Lo mismo ocurre con anyOdd
, que se establece en verdadero cuando ve 9, y seguirá siendo cierto para todas las llamadas posteriores a esa expresión.
Entonces, ¿el método abreviado &=
y |=
, o se garantiza que se llame a IsEven
en cada iteración? ¿Hay algún comportamiento definido en la especificación del lenguaje para este caso? ¿Hay algún caso de esquina en el que un cortocircuito sea problemático?
pero ¿
|=
y&=
cortocircuito también?
No. &=
y |=
son los equivalentes de las operaciones &
y |
, no de los operadores lógicos en cortocircuito.
Fácil de descubrir:
bool b = false;
b &= Foo(1);
private static bool Foo(int id)
{
Console.WriteLine("test " + id);
return false;
}
La respuesta es No, Foo()
siempre se ejecuta.
Pero si está buscando una optimización, el problema real está, por supuesto, en el bucle:
allEven = numbers.All(n => IsEven(n));
podría ser mucho más rápido. Se detendrá la iteración después de ver el primer número impar (9 en la muestra).
La especificación C # garantiza que ambos lados se evalúen exactamente una vez de izquierda a derecha y que no se produzca un cortocircuito.
5.3.3.21 Reglas generales para expresiones con expresiones incrustadas
Las siguientes reglas se aplican a este tipo de expresiones: expresiones entre paréntesis (§7.6.3), expresiones de acceso a elementos (§7.6.6), expresiones de acceso de base con indexación (§7.6.8), expresiones de incremento y decremento (§7.6.9) , §7.7.5), expresiones de conversión (§7.7.6), expresiones unarias +, -, ~, *, expresiones binarias +, -, *, /,%, <<, >>, <, <=,>, > =, ==,! =, es, como, &, |, expresiones ^ (§7.8, §7.9, §7.10, §7.11), expresiones de asignación compuesta (§7.17.2) , expresiones marcadas y sin marcar (§7.6 .12), más expresiones de creación de matrices y delegados (§7.6.10).
Cada una de estas expresiones tiene una o más subexpresiones que se evalúan incondicionalmente en un orden fijo.
La especificación de C # para operadores compuestos dice:
7.17.2 Asignación compuesta
...
Una operación de la forma
x op= y
se procesa aplicando la resolución de sobrecarga del operador binario (§7.3.4) como si la operación se escribierax op y
. Entonces,
Si el tipo de retorno del operador seleccionado es convertible implícitamente al tipo de
x
, la operación se evalúa comox = x op y
, excepto quex
se evalúa solo una vez.De lo contrario, si el operador seleccionado es un operador predefinido, si el tipo de retorno del operador seleccionado es explícitamente convertible al tipo de
x
, y siy
es implícitamente convertible al tipo dex
o el operador es un operador de cambio, entonces la operación se evalúa comox = (T)(x op y)
, donde T es el tipo dex
, excepto quex
se evalúa solo una vez....
En su caso, op
es &
o |
. El comportamiento de cortocircuito refleja el de &
/ |
y no &&
/ ||
.
Tenga en cuenta que esto solo se refiere al comportamiento visible en un escenario de un solo hilo. Por lo tanto, si el lado derecho no tiene efectos secundarios que puedan observarse en tal escenario, el compilador o JITter todavía es libre de omitir la evaluación.
En su ejemplo, el compilador es libre de terminar el ciclo una vez que conoce el resultado, ya que no hay tales efectos secundarios. Pero no es necesario hacerlo.
En particular, el tiempo no cuenta como tal efecto secundario, por lo que no puede confiar en que su código tenga un tiempo de ejecución constante. Esto puede ser problemático en un contexto de seguridad, ya que puede introducir un canal lateral de temporización.
No, los operadores &=
y |=
no están haciendo una evaluación de cortocircuito.
Son pseudooperadores, que el compilador convierte en uso del &
y |
operadores
Este codigo
allEven &= IsEven(numbers[i]);
es exactamente equivalente a:
allEven = allEven & IsEven(numbers[i]);
Si desea la comprobación de cortocircuito, debe escribirla utilizando las versiones de cortocircuito de los operadores:
allEven = allEven && IsEven(numbers[i]);
No hay un pseudo operador &&=
, pero el código anterior es exactamente lo que haría el compilador si hubiera uno.