O contra OrElse

96

¿Cuál es la diferencia entre or y OrElse ?

if temp is dbnull.value or temp = 0

produce el error:

El operador '=' no está definido para el tipo 'DBNull' y el tipo 'Integer'.

mientras este funciona como un encanto?

if temp is dbnull.value OrElse temp = 0
Si no
fuente

Respuestas:

146

OrElsees un operador de cortocircuito , Orno lo es.

Según la definición del operador booleano 'o', si el primer término es Verdadero, entonces el todo es definitivamente verdadero, por lo que no es necesario evaluar el segundo término.

OrElsesabe esto, por lo que no intenta evaluar temp = 0una vez que se establece quetemp Is DBNull.Value

Orno lo sabe y siempre intentará evaluar ambos términos. Cuando temp Is DBNull.Valueno se puede comparar con cero, se cae.

Deberías usar ... bueno, el que tenga sentido.

AakashM
fuente
2
Entonces, ¿O solo tiene sentido cuando llamo a una función después de o que tiene efectos secundarios de los que depende mi código?
Ralph M. Rickenbach
4
O tiene sentido en todos los casos en los que el segundo elemento no desencadena un error si el primero es verdadero ...
asombro
4
@ malach: Supongo que sí (realmente obtienes el comportamiento OrElse por defecto en la mayoría de los otros lenguajes): No es una buena idea llamar a funciones con efectos secundarios en condicionales compuestos, ya que hace que el código sea ilegible.
Utaal
4
@ awe: sí, pero ¿por qué quieres perder el tiempo evaluando algo que, por definición, no cambiará el resultado de la expresión?
Utaal
3
@MarkJ: Realmente no creo que cuatro caracteres adicionales interrumpan la legibilidad. Por otro lado, usar un operador que depende de la existencia de efectos secundarios (como escribió Malach) para ser significativo suena como una mala idea (¡y puede dificultar la legibilidad!). Consideraría los efectos secundarios en esos lugares como un gran no-no, y no puedo pensar en ninguna situación en la que prefiera "O" a "OrElse". Es una lástima que estos operadores funcionen de esta manera, ya que el comportamiento de "OrElse" es probablemente lo que la mayoría espera incluso cuando se usa "O" (especialmente cuando proviene de otros lenguajes).
Kjartan
42

Este es el mismo comportamiento que con C #, donde todos usan el Or codicional (||) y el Y condicional (&&), donde también tiene el O normal (|) y el Y normal (&). Entonces, comparar C # con VB.Net es:

| => O

|| => O bien

& => Y

&& => YTambién

Los operadores booleanos condifitonales son muy útiles para evitar construcciones if anidadas. Pero a veces se necesitan los operadores booleanos normales para garantizar el acceso a ambas rutas de código.

Bert Heesbeen
fuente
9
En realidad, nunca supe que esto estaba disponible. Gracias por nueva información. Es bueno saberlo, aunque realmente no veo ninguna situación en la que quiera usar "|". Creo que se necesitaría la segunda condición para que los efectos secundarios tuvieran algún sentido, ¡y eso en sí mismo tiene poco sentido en mi opinión! ;)
Kjartan
7
Eh, hasta donde yo sé, |y &son operadores bit a bit en C #, no operaciones booleanas en absoluto.
Nyerguds
8

O Else está cortocircuitado , esto significa que solo se probará un lado de la expresión si el primer lado coincide.

Al igual que AndAlso, solo probará un lado de la expresión si la primera mitad falla.

stevehipwell
fuente
4

(Miré otras respuestas y me di cuenta de que estaba terriblemente equivocado)

El operador OrElse "realiza una disyunción lógica de cortocircuito en dos expresiones", es decir: si el operando izquierdo es verdadero y así se garantiza que toda la expresión es verdadera, el operando derecho ni siquiera será evaluado (esto es útil en casos como:

string a;
//...
if (a is null) or (a = "Hi") //...

para evitar una NullReferenceException lanzada por el operando de la derecha.

Estoy sinceramente sorprendido de que esta ( evaluación perezosa ) no sea el comportamiento predeterminado de ory andcomo lo es en C / C ++ y C # (y muchos otros lenguajes ...)

Utaal
fuente
7
El caso es que, en VB Classic, solo había And y Or, que no tenían cortocircuito. Yo creo que estoy en lo correcto al decir que las primeras betas de VB.NET en realidad cambian el comportamiento de estos operadores - hubo alboroto, por lo que se cambiaron hacia atrás y AndAlso y OrElse (cortocircuito) se introdujeron. Solo puedo imaginar los nombres alternativos que deben haber considerado si estos fueran los mejores ...
AakashM
1
Al proporcionar Or y OrElse (| y || en C #), esto permite al desarrollador elegir cómo manejar su propio código. Usando el código anterior, tendría que usar un intento de captura para manejar un valor nulo en la variable a. OrElse permite al desarrollador manejar esto en el else de la instrucción if como un resultado posible conocido en lugar de una excepción. Esto es más obvio si la variable a era un parámetro en un método, donde tiene menos control sobre cuándo se asigna un valor a la variable (es decir, fuera del método)
Kevin Hogg
El cortocircuito tampoco es el comportamiento predeterminado de OR y AND en c #. c # tiene dos operadores diferentes para operaciones bit a bit y lógicas / de cortocircuito. && y || realizar comparaciones lógicas y devolver un valor booleano El & y | los operadores son bit a bit y devuelven un valor entero. Entonces, ¿dónde 1 || 2 devuelve "verdadero", 1 | 2 devuelve "3".
TomXP411
4

O bien, evalúa la primera expresión y luego, si es verdadera, procederá a la declaración, mientras que OR evalúa dos expresiones antes de continuar con su declaración.

Ejemplo:

Textbox1.Text= 4

Textbox2.Text= ""

Usando OrElse

  If TextBox1.Text > 2 OrElse TextBox2.Text > 3 Then
      MsgBox("True")
  End If

El resultado es: VERDADERO


Usando OR

 If TextBox1.Text > 2 Or TextBox2.Text > 3 Then

            MsgBox("True")
  End If

El resultado es: El error no puede convertir la cadena en doble.

Larz
fuente
3

La respuesta de Bert no es muy precisa. El '|' o '&' es un operador lógico, en C #, siempre se trata como un operador de bits, consulte el siguiente código como ejemplo

        static void Main()
        {
            object a = null;
            int b = 3;
            if (a == null | a.ToString() == "sdffd")
            {
                Console.WriteLine("dddd");
            }
            Console.WriteLine(b | b);
            Console.Read();
        }

Lo siguiente es IL

    .method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       62 (0x3e)
  .maxstack  3
  .locals init ([0] object a,
           [1] int32 b,
           [2] bool CS$4$0000)
   IL_0000:  nop
   IL_0001:  ldnull
   IL_0002:  stloc.0
   IL_0003:  ldc.i4.3
   IL_0004:  stloc.1
   IL_0005:  ldloc.0
   IL_0006:  ldnull
   IL_0007:  ceq
   IL_0009:  ldloc.0
   IL_000a:  callvirt   instance string [mscorlib]System.Object::ToString()
   IL_000f:  ldstr      "sdffd"
   IL_0014:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                 string)
   IL_0019:  or
   IL_001a:  ldc.i4.0
   IL_001b:  ceq
   IL_001d:  stloc.2
   IL_001e:  ldloc.2
   IL_001f:  brtrue.s   IL_002e
   IL_0021:  nop
   IL_0022:  ldstr      "dddd"
   IL_0027:  call       void [mscorlib]System.Console::WriteLine(string)
   IL_002c:  nop
   IL_002d:  nop
   IL_002e:  ldloc.1
   IL_002f:  ldloc.1
   IL_0030:  or
   IL_0031:  call       void [mscorlib]System.Console::WriteLine(int32)
   IL_0036:  nop
   IL_0037:  call       int32 [mscorlib]System.Console::Read()
   IL_003c:  pop
   IL_003d:  ret
    } // end of method Program::Main

cuando usas || para probar "a == null" y "a.ToString () ==" sdffd ", el IL será

 .method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       63 (0x3f)
  .maxstack  2
  .locals init ([0] object a,
           [1] int32 b,
           [2] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldnull
  IL_0002:  stloc.0
  IL_0003:  ldc.i4.3
  IL_0004:  stloc.1
  IL_0005:  ldloc.0
  IL_0006:  brfalse.s  IL_001d
  IL_0008:  ldloc.0
  IL_0009:  callvirt   instance string [mscorlib]System.Object::ToString()
  IL_000e:  ldstr      "sdffd"
  IL_0013:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                 string)
  IL_0018:  ldc.i4.0
  IL_0019:  ceq
  IL_001b:  br.s       IL_001e
  IL_001d:  ldc.i4.0
  IL_001e:  stloc.2
  IL_001f:  ldloc.2
  IL_0020:  brtrue.s   IL_002f
  IL_0022:  nop
  IL_0023:  ldstr      "dddd"
  IL_0028:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_002d:  nop
  IL_002e:  nop
  IL_002f:  ldloc.1
  IL_0030:  ldloc.1
  IL_0031:  or
  IL_0032:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0037:  nop
  IL_0038:  call       int32 [mscorlib]System.Console::Read()
  IL_003d:  pop
  IL_003e:  ret
} // end of method Program::Main

Ahora puede ver la diferencia, no piense en '|' o 'y' como operador condicional, es solo un operador lógico, no creo que sea necesario usarlo para juzgar la condición

FrankX
fuente
2
The '|' or '&' is logical operator, in C #, it always treat as bit operator. Yo también creía en esto, hasta que vi esta referencia, msdn.microsoft.com/en-us/library/kxszd0kx.aspx
user3207158
Tu respuesta está fuera de contexto. La pregunta no se relaciona con C # sino con VB, donde cuatro operadores lógicos: And, AndAlso, Or, OrElse, Not y Xor son ambos, operadores lógicos y Bitwise.
Jean-François
0

A menos que la lógica de su código requiera el comportamiento de cortocircuito que proporciona OrElse, me inclinaría por usar el operador Or porque:

  • Usar "O" es simple y requiere menos escritura.
  • El ahorro de tiempo computacional de usar OrElse es insignificante en la mayoría de los casos.
  • Lo más importante es que el uso de OrElse puede ocultar errores en cláusulas posteriores que pueden no ser revelados inicialmente hasta que la lógica del programa finalmente cumpla esas condiciones.
KnowKnot
fuente
0

La razón por la que falla la compilación en el ejemplo es el orden de las operaciones.

El analizador de expresiones está intentando evaluar primero "dbnull.value o temp".

if temp is (dbnull.value or temp) = 0

El error está aquí, porque no puede hacer un OR bit a bit entre un entero (temp) y dbnull.value.

OrElse corrige esto, no porque esté cortocircuitado, sino porque es más bajo en el orden de las operaciones , por lo que "temp is dbnull.value" y "3 = 0" se evalúan primero, en lugar de que el analizador intente comparar dbNull y temperatura

Entonces, la evaluación con OrElse funciona como espera: (suponga temp = 3)

if temp is dbnull.value OrElse temp = 0 then
if 3 is dbnull.value OrElse 3 = 0 then
if false OrElse 3=0 then
if false OrElse false then
if false then

Esto fue en realidad en un examen de ingreso en una empresa de software para la que solía trabajar, y era un problema común que solía encontrar en VB6. Por lo tanto, es una buena idea poner entre paréntesis las subexpresiones cuando se usan operadores booleanos:

Esto se habría compilado correctamente:

if (temp is dbnull.value) Or (temp = 0) then 

Aunque, como ya han señalado todos, OrElse y AndAlso son realmente los operadores correctos para usar en este contexto.

TomXP411
fuente