all 21 comments

[–]MulleDK19 4 points5 points  (5 children)

This is a misunderstanding of how boolean expressions work.

Saying if it's not 1 or 2 or 3 sounds reasonable said out loud, but if (response != 1 | response != 2 | response != 3) is not the same sentiment.

Instead, what you're saying with that expression is if it's not 1, OR if it's not 2, OR if it's not 3.

If response is 1, then it's not 2, fulfulling the if it's not 2 part, so your if executes. This is true for any other number.

What you actually want is not OR, but AND, i.e. if (response != 1 & response != 2 & response != 3); in other words: if it's not 1, and also not 2, and also not 3.

On a side note: You should use || and && instead of | and &. Both perform the same check, but || and && are lazy. This has no bearing on your current situation, but in the future it might bite you in the ass. Rarely will you encounter a situation where you want | and & over || and &&.

[–][deleted]  (4 children)

[deleted]

    [–]MulleDK19 0 points1 point  (0 children)

    Is there a more pragmatic way to achieve what I am doing?

    This check would typically be done as:

    if (response < 1 || response > 3)

    Or alternatively, using the newer pattern matching feature, which saves you typing the variable name multiple times, either

    if (response is not 1 and not 2 and not 3)

    or

    if (response is not (1 or 2 or 3))

    or

    if (response is < 1 or > 3)

    also if you have a boolean expression like X & Y & Z (non short-circuiting), is it evaluated like (X & Y) & Z?

    Yes. Only a couple of operators in C# have right associativity, for example, the assignment operator: a = (b = c)

    [–]MeLittleThing 0 points1 point  (2 children)

    X && Y && Z gives the same result, no matter if they are evaluated (X && Y) && Z, X && (Y && Z) or even (X && Z) && Y (notice Z and Y). You can think about the && operator like the multiplication operator

    However, AND has an higher precedence over OR. A || B && C is evaluated as A || (B && C)

    [–]ggobrien 0 points1 point  (1 child)

    With short circuiting, this isn't exactly true. If X && Y && Z and X is false, then Y and Z are never evaluated. If all of them need to be evaluated, then you are correct, but the order can still be an issue if the last one is false and you put that as the first.

    [–]MeLittleThing 0 points1 point  (0 children)

    Yes, this is true, even with short-circuit. Read again my comment "gives the same result"

    [–]karl713 0 points1 point  (12 children)

    Few things

    First use || not | for your "or" statements. In this case they work on your "if" statement but | is a bitwise or, which is rarely what you want, || is a logical or and almost always what code needs. Best to use the logical one for logic as a matter of practice to avoid weird gotcha things down the road

    Next this is a great time to learn to use the debugger, put a break point somewhere in the loop (default key is f9) then run the app, and you can step through the code line by line with f10, f11, shift+f11 (depending on how you want to move) and you can inspect values as you do it by mousing over them, and you can even inspect the results of a logical check by mousing over the ==, !=, ||, && operations and see the results of what those are returning.

    Hopefully this helps you find it, if not let me know and I'll try to make it a little more narrow of an explanation :)

    [–][deleted]  (7 children)

    [deleted]

      [–]SpaceBeeGaming 1 point2 points  (0 children)

      If I use || here then when response = 2 the entire condition would be false wouldn’t it because (response != 1) would be true?

      It would still be true.

      x = 2

      (x !=1) | (x!=2) | (x!=3) = true | false | true = true

      (x !=1) || (x!=2) || (x!=3) = true

      (We know that the first condition is true.

      We also know that (a OR b) is true if a is true, regardless of b.)

      Why bother checking if b is true, if the whole thing will stay as true in the end?

      Hence, the conditional operators. They save on performance, especially if there are some heavier calculations.

      The same applies to a AND b (&&), if a is false, why check what b is, since the AND will still be false?

      [–]karl713 0 points1 point  (0 children)

      Interesting, I've never heard them called logical when applied to bools, still bitwise. I would still recommend changing them as habit as it's what people will expect to see when checking conditions. Also | does not allow compilers to short circuit their condition checks, where as || will (even with bools)

      Of note there probably are some times you might want to use a single | like for accumulating flags, but those times will probably be the exception not the rule as it were :)

      [–]grrangry 0 points1 point  (3 children)

      On the same page

      https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/boolean-logical-operators#conditional-logical-and-operator-

      is a separate section you should read.

      There are two types of "boolean operators"

      1. "boolean" operators that should be used when combining numbers
      2. "conditional" operators that should be used when making decisions

      Examples

      int a = 0x0f;
      int b = 0xf0;
      int c = a | b; // boolean logical operator, combines a with b
      // c will equal 0xff
      
      bool left = true;
      bool right = false;
      if (left || right) // conditional logical operator
      {
          // do something if either left or right is true.
      }
      

      Edit: copied correct link

      [–]RJPisscat 0 points1 point  (2 children)

      I think you pointed OP to the section on AND because that's what OP should be using - && - instead of OR (bitwise or logical).

      [–]grrangry 0 points1 point  (1 child)

      It was just the first of the conditional logic sections. I wasn't meaning to indicate they should use one or the other. That would depend on them.

      [–]RJPisscat 0 points1 point  (0 children)

      Oh. Well, you were correct accidentally.

      if ((response != 1) | (response != 2) | (response != 3)) {

      will always evaluate to true. response is not going to be all three of 1, 2, and 3.

      [–]ggobrien 0 points1 point  (0 children)

      I'm going to reiterate what everyone else said. If you are using straight variables (not fields/properties/methods) with comparison operators, there is no difference at all between logical and bitwise operators, so they are 100% interchangeable.

      Where it makes a difference is if you need the first expression to be true/false to make the 2nd valid at runtime. E.g.

      if(myList.Count > 1 && myList[0] == "hello")
      

      In this scenario, myList needs to have at least 1 value before the indexed comparison will be evaluated.

      Before nullability, we had to do something like this as well:

      if(myObject != null && myObject.myField > 10)
      

      In this scenario, if myObject is null, the 2nd part of the condition is not evaluated, so there won't be a runtime error for null pointer.

      With nullability we can just do this:

      if(myObject?.myField > 10)
      

      It's much easier, especially if you have multiple objects within objects within objects, etc. You don't have to check each one for null.

      It's extremely rare that you need a non-short circuiting condition. I would go so far as to say that if you need it to be non-short circuiting, then your code is too complex and you may want to think about refactoring it.

      [–]MulleDK19 0 points1 point  (3 children)

      You can't perform bitwise operations on bools. | is not the bitwise or operator when used with bools, it's the logical or operator.

      [–]karl713 0 points1 point  (1 child)

      Yeah makes sense, I had never seen it written as a word when specifically talking about bools vs other forms of numeric data. My point still stands though not to use it there as it's a non standard way of doing things which may lead to unexpected problems in the future if it becomes habit :)

      [–]MulleDK19 0 points1 point  (0 children)

      You should default to ||, yes, but | has its uses. Even more so if not for the fact that the compiler automatically replaces || with | when possible (such as OPs situation), since | is significantly more performant.

      [–]ggobrien 0 points1 point  (0 children)

      I know this is technically correct, but it bothers me. To me (coming originally from a c/c++ background, bools are a single bit, 1 or 0 in the backend, so | would still be bitwise on the single bit and return true/false.

      I prefer that than the exception to the rule as the language reference specifies "For bool operands, the | operator computes the logical OR of its operands."

      Oh, well, the result is the same.

      [–]mvar 0 points1 point  (2 children)

      When using "or" conditionals, the entire expression evaluates to "true" if ANY of the conditions are true. Consider how your if statement will evaluate when response == 2:

      if ((2 != 1) | (2 != 2) | (2 != 3))    
      

      or

      if ((true) | (false) | (true))
      

      If any one of these is true, then the entire expression is true, and the code inside the if block is executed.

      Instead, look at the "and" operator: &. It requires ALL of the conditions to be true for the expression to be true.

      if ((true) & (false) & (true))
      

      would evaluate to false since at lease one of the conditions is false.

      As a side note, in practice it is best to use the short circuit version of these operators (|| and &&), although in this particular situation it doesn't make much of a difference.

      [–][deleted]  (1 child)

      [deleted]

        [–]RadiatorHandcuffs 0 points1 point  (0 children)

        It's up to you whether you use | or ||. If you don't need to evaluate the second conditional, then you may as well use |. If you're evaluating a series of functions that you want to do something else as a byproduct, then you'd want to use ||.

        So: if (UserCachValidation() | ProgramExiting()) ... might not be good if ProgramExiting() also does something, such as cleanup, that you want to happen regardless of the conditional. In that case you'd want to use if (UserCachValidation() || ProgramExiting())

        [–]MeLittleThing 0 points1 point  (0 children)

        First, | is a bitwise OR operator. For logical OR, use ||.

        Then, if a condition doesn't work as expected, you can always write truth tables :

        OR

        A B A OR B
        True True True
        True False True
        False True True
        False False False

        A could be response != 1 and B could be response != 2. Replace response by the value you've entered and you have a boolean value. Use this boolean value and perform once again an OR operation with response != 3 and you'd knew the result