remarks example cref c# operators

example - remarks c#



¿Por qué ''&&'' y no ''&''? (16)

¿Por qué es && preferible para & y || preferible a | ?

Le pregunté a alguien que ha estado programando durante años y su explicación fue:

Por ejemplo, en if (bool1 && bool2 && bool3) { /*DoSomething*/ } , bool1 tiene que ser verdadero para probar bool2 que tiene que ser verdadero antes de pasar a bool3 , etc. Si hubiera usado un solo & cambio, no hay orden para la prueba, incluso si todos deben ser ciertos para pasar a la siguiente línea, entonces, ¿por qué importa de todos modos?

Nota: me gustaría señalar que soy el equivalente de programación de un niño pequeño y esta no es una pregunta seria o urgente. Es más una cuestión de entender por qué las cosas deberían hacerse de una manera determinada en lugar de otra.


&& y & significan dos cosas muy diferentes y le dan dos respuestas diferentes.

1 && 2 rinde 1 ("verdadero")
1 & 2 rinden 0 ("falso")

&& es un operador lógico - significa "verdadero si ambos operandos son verdaderos"
& es una comparación bit a bit Significa "dime cuál de los bits se establece en ambos operandos"


Corto y simple:

1 && 2 = verdadero
porque
1 = verdadero (distinto de cero) en C
2 = verdadero (distinto de cero) en C

true ANDS lógicamente con true para dar true .

Pero

1 & 2 = 0 = falso
porque
1 = 0001 en binario
2 = 0010 en binario

0001 ANDs bitwise con 0010 para dar 0000 = 0 en decimal.

Del mismo modo para || y | operadores también ...!


Cuando se utiliza en una expresión lógica como, por ejemplo, una sentencia if && preferible porque dejará de evaluar expresiones tan pronto como se encuentre el primer resultado falso. Esto es posible porque un valor falso hará que toda la expresión sea falsa. Del mismo modo (y de nuevo en expresiones lógicas) || es preferible porque dejará de evaluar expresiones tan pronto como encuentre una expresión verdadera porque cualquier valor verdadero hará que toda la expresión sea verdadera.

Sin embargo, si las expresiones siendo or-ed o ed juntas tienen efectos secundarios, y desea que todo esto suceda como resultado de su expresión (independientemente del resultado de la expresión lógica), entonces & y | puede ser usado. Por el contrario, el && y || los operadores pueden ser útiles como protectores contra los efectos colaterales no deseados (como un puntero nulo que causa el lanzamiento de una excepción).

The & y | los operadores también se pueden usar con enteros y en este caso producen un resultado entero que son los dos operandos ed-ed u or-ed juntos en el nivel de bit. Esto puede ser útil cuando los bits binarios de un valor entero se usan como una matriz de valores verdaderos y falsos. Para probar si un cierto bit está activado o desactivado, una máscara de bits se administra en bit y se edita con el valor. Para encender un poco, la misma máscara puede ser bit-or-ed con el valor. Finalmente para apagar un poco, el complemento bit a bit (usando ~ ) de una máscara es bitwise y ed con el valor.

int a = 0; // 0 means all bits off a = a | 4; // set a to binary 100 if ((a & 4) != 0) { // will do something } a = a & (~4) // turn bit off again, a is now 000

En otros lenguajes además de C #, se debe tener cuidado con los modos lógico y bit a bit de & y |. En el código anterior, la expresión condicional de la declaración if (a & 4) != 0 es una forma segura de expresar esta condición, pero en muchos lenguajes C, los enunciados condicionales simplemente pueden tratar valores enteros cero como valores enteros falsos y no-cero como verdadero (La razón de esto se relaciona con las instrucciones de procesador de ramificación condicional disponibles, y su relación con el indicador de cero que se actualiza después de cada operación entera). De ìf que la prueba de la declaración de cero se puede eliminar y la condición se puede acortar a (a & 4) .

Esto podría causar confusión e incluso problemas cuando las expresiones se combinen utilizando los valores de retorno de bits y del operador que no tienen bits alineados. Considere el siguiente ejemplo donde se desean los efectos colaterales de dos funciones, antes de verificar que ambos fueron exitosos (según lo definido por ellos al devolver un valor distinto de cero):

if (foo() & bar()) { // do something }

En C, si foo() devuelve 1 y bar() devuelve 2, el "algo" no se hará porque 1 & 2 son cero.

C # requiere sentencias condicionales como if tuviera un boenoque oeprand, y el lenguaje no permite que un valor entero sea convertido a un valor booleano. Entonces el código anterior generaría errores de compilación. Se expresaría más correctamente de la siguiente manera:

if (foo() != 0 & bar() != 0) { // do something }


En el caso de:

if (obj != null && obj.Property == true) { }

funcionaría como se esperaba

Pero:

if (obj != null & obj.Property == true) { }

podría lanzar una excepción de referencia nula.


En la mayoría de los casos, && y || son preferidos sobre & y | porque los primeros están en cortocircuito, lo que significa que la evaluación se cancela tan pronto como el resultado es claro.

Ejemplo:

if(CanExecute() && CanSave()) { }

Si CanExecute devuelve false , la expresión completa será false , independientemente del valor de retorno de CanSave . Debido a esto, CanSave no se ejecuta.

Esto es muy útil en la siguiente circunstancia:

string value; if(dict.TryGetValue(key, out value) && value.Contains("test")) { // Do Something }

TryGetValue devuelve false si la clave suministrada no se encuentra en el diccionario. Debido a la naturaleza de cortocircuito de && , value.Contains("test") solo se ejecuta, cuando TryGetValue devuelve true y, por lo tanto, el value no es null . Si utilizara el operador AND bit a bit & , en cambio, obtendría una NullReferenceException si la clave no se encuentra en el diccionario, porque la segunda parte de la expresión se ejecuta en cualquier caso.

Un ejemplo similar pero más simple de esto es el siguiente código (como lo menciona TJHeuvel):

if(op != null && op.CanExecute()) { // Do Something }

CanExecute solo se ejecuta si op no es null . Si op es null , la primera parte de la expresión ( op != null ) se evalúa como false y la evaluación del resto ( op.CanExecute() ) se omite.

Aparte de esto, técnicamente, también son diferentes:
&& y || solo se puede usar en bool mientras que & y | se puede usar en cualquier tipo integral ( bool , int , long , sbyte , ...), ya que son operadores bit a bit. & es el operador Y a nivel de bit y | es el operador O bit a bit .

Para ser muy exactos, en C #, esos operadores ( & , | [y ^ ]) se llaman "Operadores lógicos" (ver la especificación de C # , capítulo 7.11). Hay varias implementaciones de estos operadores:

  1. Para enteros ( int , uint , long y ulong , capítulo 7.11.1):
    Se implementan para calcular el resultado bit a bit de los operandos y el operador, es decir, se implementa para calcular el AND lógico a nivel de bit, etc.
  2. Para enumeraciones (capítulo 7.11.2):
    Se implementan para realizar la operación lógica del tipo subyacente de la enumeración.
  3. Para los bools y los boles con capacidad para nulos (capítulos 7.11.3 y 7.11.4):
    El resultado no se calcula usando cálculos bit a bit. El resultado se busca básicamente en función de los valores de los dos operandos, ya que el número de posibilidades es muy pequeño.
    Debido a que ambos valores se usan para la búsqueda, esta implementación no está en cortocircuito.

Es importante, porque si el costo de la evaluación de bool2 (por ejemplo) es alto, pero bool1 es falso, entonces te has ahorrado un poco de computación usando && sobre &


Explicar muy claramente lo que esto significa (aunque las otras respuestas lo insinúan, pero probablemente utilices terminología que no entiendes).

El siguiente código:

if (a && b) { Foo(); }

Está realmente compilado a esto:

if (a) { if (b) { Foo(); } }

Donde se compila el siguiente código exactamente como se representa:

if (a & b) { Foo(); }

Esto se llama cortocircuito. En general, siempre debes usar && y || en tus condiciones

Marcas de bonificación: hay un escenario en el que no deberías. Si se encuentra en una situación en la que el rendimiento es crucial (y esto es crucial para los nano segundos ) utilice solo un cortocircuito cuando sea necesario (p. Ej., Comprobación null ), ya que un cortocircuito es una derivación / salto; que podría dar lugar a una predicción errónea de rama en su CPU; an & es mucho más barato que && . También hay un escenario en el que el cortocircuito puede realmente romper la lógica: eche un vistazo a esta respuesta mía.

Diatriba / Monólogo : En cuanto a la ramificación de la predicción equivocada que la mayoría ignora felizmente. Citando a Andy Firth (que ha estado trabajando en juegos durante 13 años): "Este puede ser el nivel más bajo en el que las personas piensan que deben ir ... pero estarían equivocados. Comprender cómo el hardware que estás programando para las ramas trata afectan el desempeño en GRAN grado ... mucho más de lo que la mayoría de los programadores pueden apreciar: muerte por mil cortes ".

  • Los desarrolladores de juegos (y otros que trabajan en condiciones extremas en tiempo real) llegan hasta la reestructuración de su lógica para adaptarse mejor al predictor. También hay evidencia de esto en el código mscorlib descompilado.
  • El hecho de que .NET lo proteja de este tipo de cosas no significa que no sea importante. Una mala predicción de derivación es terriblemente costosa a 60 Hz; o a 10,000 solicitudes / segundo.
  • Intel no tendría herramientas para identificar la ubicación de las predicciones erróneas, ni Windows tendría un contador de rendimiento para esto, ni habría una palabra para describirlo, si no fuera un problema.
  • La ignorancia sobre los niveles inferiores y la arquitectura no hace que alguien que los conoce se equivoque.
  • Siempre trate de comprender las limitaciones del hardware en el que está trabajando.

Aquí hay un punto de referencia para los no creyentes. Lo mejor es ejecutar el proceso en RealTime / High para mitigar que el programador tenga un efecto: https://gist.github.com/1200737


La forma más rápida (y ligeramente simplificada) de explicar esto a las personas que NO NECESITAN conocer las operaciones exactas del código cuando hacen esto

&& está haciendo un control de cada una de esas condiciones hasta que encuentre un falso y devuelva el resultado completo como falso

|| está haciendo un control de cada una de esas condiciones hasta que encuentre un verdadero y devuelva el resultado completo como verdadero.

& está haciendo MATHS basado en ambas condiciones y tratando con el resultado.

| está haciendo MATHS basado en ambas condiciones y tratando con el resultado.

Nunca me he encontrado con un punto donde haya necesitado usar & o | dentro de una declaración if. Lo uso principalmente para cortar valores hexadecimales en sus colores componentes usando un desplazamiento bit a bit.

P.EJ:

r = fullvalue >> 0xFF & 0xFF; g = fullvalue >> 0xF & 0xFF; b = fullvalue & 0xFF;

Dentro de esta operación, "& 0xFF" obliga a solo mirar el valor binario. Personalmente no he encontrado un uso para | sin embargo.


OK, en valor nominal

Boolean a = true; Boolean b = false; Console.WriteLine("a({0}) && b({1}) = {2}", a, b, a && b); Console.WriteLine("a({0}) || b({1}) = {2}", a, b, a || b); Console.WriteLine("a({0}) == b({1}) = {2}", a, b, a == b); Console.WriteLine("a({0}) & b({1}) = {2}", a, b, a & b); Console.WriteLine("a({0}) | b({1}) = {2}", a, b, a | b); Console.WriteLine("a({0}) = b({1}) = {2}", a, b, a = b);

producir la misma respuesta Sin embargo, como mostraste, si tienes una pregunta más compleja, entonces:

if (a and b and c and d) ..

Si a no es verdad y quizás b es una función donde tiene que sonar, conéctese a algo, obtenga esto, haga eso, tome una decisión ... ¿para qué molestarse? Pérdida de tiempo, ya sabes que ya ha fallado. ¿Por qué hacer que la máquina se apague y hacer un trabajo extra sin sentido?

Siempre he usado && porque pongo el que probablemente falle primero, ergo, menos cálculos antes de continuar cuando no tiene sentido. Si no hay forma de predecir elecciones menos probables, como por ejemplo tener un booleano para limitar la salida de datos, algo como:

if (limit && !MyDictionary.ContainsKey("name")) continue;

Si no es un limit , no se moleste en buscar la clave, lo que podría llevar más tiempo.


Operador lógico ( || y && ) vs. operador bit a bit ( | y & ).

La diferencia más importante entre un operador lógico y un operador bit a bit es que un operador lógico toma dos booleanos y produce un booleano mientras que un operador bit a bit toma dos enteros y produce un entero (nota: enteros significa cualquier tipo de datos integrales, no solo int).

Para ser pedante, un operador bit a bit toma un patrón de bits (p. Ej., 01101011) y hace un bit Y / O en cada bit. Entonces, por ejemplo, si tiene dos enteros de 8 bits:

a = 00110010 (in decimal: 32+16+2 = 50) b = 01010011 (in decimal: 64+ 16+2+1 = 83) ---------------- a & b = 00010010 (in decimal: 16+2 = 18) a | b = 01110011 (in decimal: 64+32+16+2+1 = 115)

mientras que un operador lógico solo funciona en bool :

a = true b = false -------------- a && b = false a || b = true

Segundo, a menudo es posible usar un operador bit a bit en bool ya que verdadero y falso es equivalente a 1 y 0 respectivamente, y sucede que si traduces verdadero a 1 y falso a 0, entonces haz una operación bit a bit y luego conviertes a cero a verdadero y cero a falso; sucede que el resultado será el mismo si hubiera usado el operador lógico (verifique esto para hacer ejercicio).

Otra distinción importante es también que un operador lógico está cortocircuitado . Por lo tanto, en algunos círculos [1], a menudo ves a personas que hacen algo como esto:

if (person && person.punch()) { person.doVictoryDance() }

que se traduce en: "si la persona existe (es decir, no es nula), intente golpearlo, y si el golpe tiene éxito (es decir, regresa verdadero), entonces realice una danza de la victoria" .

Si hubiera usado un operador bit a bit en su lugar, esto:

if (person & person.punch()) { person.doVictoryDance() }

se traducirá a: "si la persona existe (es decir, no es nula) y el golpe tiene éxito (es decir, vuelve verdadero), entonces haz una danza de la victoria" .

Tenga en cuenta que en el operador lógico cortocircuitado, el código person.punch() no se puede ejecutar en absoluto si la person es nula. De hecho, en este caso particular, el segundo código produciría un error de referencia nulo si la person es nula, ya que intenta llamar a person.punch() sin importar si la persona es nula o no. Este comportamiento de no evaluar el operando correcto se denomina cortocircuito .

[1] Algunos programadores se negarán a poner una llamada a función que tenga un efecto secundario dentro de una expresión if , mientras que para otros es una expresión común y muy útil.

Dado que un operador bit a bit trabaja en 32 bits a la vez (si está en una máquina de 32 bits), puede conducir a un código más elegante y más rápido si necesita comparar una gran cantidad de condiciones, por ejemplo

int CAN_PUNCH = 1 << 0, CAN_KICK = 1 << 1, CAN_DRINK = 1 << 2, CAN_SIT = 1 << 3, CAN_SHOOT_GUNS = 1 << 4, CAN_TALK = 1 << 5, CAN_SHOOT_CANNONS = 1 << 6; Person person; person.abilities = CAN_PUNCH | CAN_KICK | CAN_DRINK | CAN_SIT | CAN_SHOOT_GUNS; Place bar; bar.rules = CAN_DRINK | CAN_SIT | CAN_TALK; Place military; military.rules = CAN_SHOOT_CANNONS | CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT; CurrentLocation cloc1, cloc2; cloc1.usable_abilities = person_abilities & bar_rules; cloc2.usable_abilities = person_abilities & military_rules; // cloc1.usable_abilities will contain the bit pattern that matches `CAN_DRINK | CAN_SIT` // while cloc2.usable_abilities will contain the bit pattern that matches `CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT`

Hacer lo mismo con los operadores lógicos requeriría una cantidad incómoda de comparaciones:

Person person; person.can_punch = person.can_kick = person.can_drink = person.can_sit = person.can_shoot_guns = true; person.can_shoot_cannons = false; Place bar; bar.rules.can_drink = bar.rules.can_sit = bar.rules.can_talk = true; bar.rules.can_punch = bar.rules.can_kick = bar.rules.can_shoot_guns = bar.rules.can_shoot_cannons = false; Place military; military.rules.can_punch = military.rules.can_kick = military.rules.can_shoot_guns = military.rules.can_shoot_cannons = military.rules.can_sit = true; military.rules.can_drink = military.rules.can_talk = false; CurrentLocation cloc1; bool cloc1.usable_abilities.can_punch = bar.rules.can_punch && person.can_punch, cloc1.usable_abilities.can_kick = bar.rules.can_kick && person.can_kick, cloc1.usable_abilities.can_drink = bar.rules.can_drink && person.can_drink, cloc1.usable_abilities.can_sit = bar.rules.can_sit && person.can_sit, cloc1.usable_abilities.can_shoot_guns = bar.rules.can_shoot_guns && person.can_shoot_guns, cloc1.usable_abilities.can_shoot_cannons = bar.rules.can_shoot_cannons && person.can_shoot_cannons cloc1.usable_abilities.can_talk = bar.rules.can_talk && person.can_talk; bool cloc2.usable_abilities.can_punch = military.rules.can_punch && person.can_punch, cloc2.usable_abilities.can_kick = military.rules.can_kick && person.can_kick, cloc2.usable_abilities.can_drink = military.rules.can_drink && person.can_drink, cloc2.usable_abilities.can_sit = military.rules.can_sit && person.can_sit, cloc2.usable_abilities.can_shoot_guns = military.rules.can_shoot_guns && person.can_shoot_guns, cloc2.usable_abilities.can_talk = military.rules.can_talk && person.can_talk, cloc2.usable_abilities.can_shoot_cannons = military.rules.can_shoot_cannons && person.can_shoot_cannons;

Un ejemplo clásico donde se usan patrones de bits y operadores bit a bit es en los permisos del sistema de archivos Unix / Linux.


Porque && y || se usan para controlar el flujo como if/else . No siempre se trata de condicionales. Es perfectamente razonable escribir como una afirmación, no como un condicional if o if , lo siguiente:

a() && b() && c() && d();

o incluso

w() || x() || y() || z();

No es solo que sean más fáciles de escribir que las versiones equivalentes de if/else ; también son mucho más fáciles de leer y entender.


Si eres un programador de C antiguo, ten cuidado . C # realmente me ha sorprendido.

MSDN dice que para | operador:

Binario | los operadores están predefinidos para los tipos integrales y bool . Para tipos integrales, | calcula el OR bit a bit de sus operandos. Para operandos bool, | calcula el OR lógico de sus operandos; es decir, el resultado es falso si y solo si sus dos operandos son falsos.

(El énfasis es mío). Los tipos booleanos se manejan de manera especial, y en este contexto, la pregunta solo comienza a tener sentido, y la diferencia es que, como otros ya se han expandido en sus respuestas:

&& y || están en cortocircuito. & y | evaluar ambos operandos.

y lo que es preferible depende de muchas cosas como los efectos secundarios, el rendimiento y la legibilidad del código, pero en general los operadores de cortocircuito son preferibles también porque los entienden mejor personas con antecedentes similares a mí.

La razón es: argumento de esta manera: dado que no hay ningún tipo de booleano real en C, podría usar el operador de bit a bit | y tiene su resultado evaluado como verdadero o falso en una condición if. Pero esta es la actitud incorrecta para C #, porque ya existe un caso especial para los tipos booleanos.


Simplemente,

if exp1 && exp2

si exp1 es flase , no verifique exp2

pero

if exp1 & exp2

si exp1 es false O true cheque exp2

y rara vez las personas usan & porque rara vez quieren verificar exp2 si exp1 es false


&& es la versión de cortocircuito de & .

Si estamos evaluando false & true , ya sabemos por el primer argumento que el resultado será falso. La versión && del operador devolverá un resultado tan pronto como sea posible, en lugar de evaluar la expresión completa. También hay una versión similar de | operador, || .


Los operadores de C # deben explicar por qué:

Esencialmente tiene dos & o | ''s significa que es un condicional en lugar de un lógico, por lo que puede ver la diferencia entre los dos.

& Operator tiene un ejemplo de usar one & .


if (list.Count() > 14 && list[14] == "foo")

es seguro

if (list.Count() > 14 & list[14] == "foo")

se bloqueará si la lista no tiene el tamaño correcto.