#1,201 – Writing Conditional Logical Operators as Conditionals

The conditional logical operators (&&, ||) can short-circuit evaluation, based on the value of the left side of the expression.

  • For &&, evaluation short-circuits if left-side is false (result is false)
  • For ||, evaluation short-circuits if left-side is true (result is true)

You can also think of the conditional logical operators as being equivalent to the ternary conditional operator.

            bool leftSide = true;
            bool rightSide = false;

            // Short-circuits (right side not evaluated)
            bool bResult = leftSide || rightSide;

            // || Equivalent to
            bResult = leftSide ? true : rightSide;

            // Short-circuits if left side is false
            bResult = leftSide && rightSide;

            // && Equivalent to
            bResult = leftSide ? rightSide : false;
Advertisement

#1,200 – Logical Operators vs. Conditional Logical Operators

You can use either the logical operators (|, &) or the conditional logical operators (||, &&) when comparing two boolean values (or expressions).

            bool isFromMN = true;
            bool likesConfrontation = false;

            bool bResult = likesConfrontation & isFromMN;  // false
            bResult = likesConfrontation && isFromMN;      // false
            bResult = likesConfrontation | isFromMN;       // true
            bResult = likesConfrontation || isFromMN;       // true

The difference between these operators, when used with boolean values, is that the conditional logical operators can short-circuit evaluation, avoiding evaluation of the right side of the expression if possible.

            int val = 0;

            // Short-circuits (right side not evaluated)
            bResult = isFromMN || ((5 / val) == 1);     // true 

            // Throws exception (does not short-circuit)
            bResult = isFromMN | ((5 / val) == 1);

#1,114 – Don’t Use Shift Operators to Do Multiplication

When you use the shift operators on integer-based values, you effectively multiply a number by two when you do a left shift by one bit and divide a number by two when you do a right shift by one bit.

In years past, you’d sometimes hear a recommendation that you should prefer using the shift operators over multiplicative operators because the shift operators were faster.

With modern optimizing compilers, you should no longer worry about this level of optimization and performance.  If you need to multiply a number by a power of two, you should express your intent by using the multiplicative operators.  (E.g. “* 2” or “* 4”).  The compiler will figure out that fastest way to accomplish this.

#1,110 – The Bitwise Exclusive OR Operator

You can use the ^ operator to do a bitwise exclusive OR (“XOR”) operation between two integer-based values.

An exclusive OR operation is applied to two different bits, returning true if exactly one of the input bits is true.  Here is a truth table showing the output value for all possible input combinations.

  • 0 ^ 0 = 0
  • 0 ^ 1 = 1
  • 1 ^ 0 = 1
  • 1 ^ 1 = 0

You can use the ^ operator on two arbitrary integer values as shown below.  The operation is applied to the two integer values, one bit at a time.

            int n1 = 12;
            int n2 = 10;
            int result = n1 ^ n2;
            Console.WriteLine("{0} ^ {1} = {2}", n1, n2, result);

1110-001
It may help to use hex notation in order to better understand how the exclusive OR operation works on each bit.

            int n1 = 0xC;  // 12 (dec) or 1100 (bin)
            int n2 = 0xA;  // 10 (dec) or 1010 (bin)

            // Result = 0110 bin (6 dec)
            int result = n1 ^ n2;

1110-002

#1,109 – The Bitwise NOT Operator

You can use the ~ operator to do a bitwise NOT operation on an integer-based value.

A NOT operation is applied to a single input bit, returning true if the input bit is false, or false if it is true.  The NOT operator “flips” or “negates” the input bit.  Here is a truth table showing the output value for the possible input values.

  • ~0  = 1
  • ~1 = 0

You can use the ~ operator on an arbitrary integer value as shown below.  The NOT operation will be applied to the integer value, one bit at a time.

            int n = 5;
            int result = ~n;
            Console.WriteLine("~{0} = {1}", n, result);

1109-001
It may help to use hex notation in order to better understand how the NOT operation works on each bit.

            int n = 0x00000005;
            int result = ~n;
            Console.WriteLine(  "~{0:X8} = {1:X8}", n, result);

1109-002

1109-003

#1,108 – The Bitwise OR Operator

You can use the | operator to do a bitwise OR operation between two integer-based values.

An OR operation is applied to two different bits, returning true if either one or both input bits are true.  Here is a truth table showing the output value for all possible input combinations.

  • 0 | 0 = 0
  • 0 | 1 = 1
  • 1 | 0 = 1
  • 1 | 1 = 1

You can use the | operator on two arbitrary integer values as shown below.  The OR operation will be applied to the two integer values, one bit at a time.

            int n1 = 12;
            int n2 = 10;
            int result = n1 | n2;
            Console.WriteLine("{0} | {1} = {2}", n1, n2, result);

1108-001
It may help to use hex notation in order to better understand how the OR operation works on each bit.

            int n1 = 0xC;  // 12 (dec) or 1100 (bin)
            int n2 = 0xA;  // 10 (dec) or 1010 (bin)

            // result = 1110
            int result = n1 | n2;

1108-002

#1,107 – The Bitwise AND Operator

You can use the & operator to do a bitwise AND operation between two integer-based values.

An AND operation is applied to two different bits, returning true if both input bits are true.  Here is a truth table showing the output value for all possible input combinations.

  • 0 & 0 = 0
  • 0 & 1 = 0
  • 1 & 0 = 0
  • 1 & 1 = 1

You can use the & operator on two arbitrary integer values as shown below.  The AND operation will be applied to the two integer values, one bit at a time.

            int n1 = 12;
            int n2 = 10;
            int result = n1 & n2;
            Console.WriteLine("{0} & {1} = {2}", n1, n2, result);

1107-001
It may help to use hex notation in order to better understand how the AND operation works on each bit.

            int n1 = 0xC;  // 12 (dec) or 1100 (bin)
            int n2 = 0xA;  // 10 (dec) or 1010 (bin)

            // result = 1000
            int result = n1 & n2;

1107-002

#1,100 – Increment and Decrement Operators Are Not Thread-Safe

If you have multiple threads increment or decrementing a variable using the increment or decrement operators, you may not get the desired result.  These operators are not thread-safe.

Imagine two threads that increment a variable.  If they do the operation serially, the variable ends up correctly incremented twice.  If the two threads read the value at the same time, however, they may both end up writing the same value back to the variable.  The variable ends up incremented just once.

You can do a safe increment operation using System.Threading.Threadlocked.Increment.

        private static int counter1 = 0;
        private static int counter2 = 0;

        static void Main(string[] args)
        {
            int maxExclusive = 1001;

            Parallel.For(1, maxExclusive, n =>
            {
                // Do some work here.
                Thread.Sleep(4);
                counter1++;
                Interlocked.Increment(ref counter2);
            });

            Console.WriteLine(
                "Ran {0} iterations, counter 1 = {1}, counter 2 = {2}",
                maxExclusive - 1, counter1, counter2);

            Console.ReadLine();
        }

Note below that Counter #1 is not incremented properly.
1100-001

#1,099 – Overloading the Increment Operator

You can overload the increment (++) operator in a class, providing custom increment functionality for an object.

The example below shows an overloaded increment operator defined in a Dog class.  The effect is to add 1 to the age of the dog.  We are careful to return a reference to the object that was passed in so that no other data in the instance changes.

    public class Dog 
    {
        // Increment a Dog
        public static Dog operator ++(Dog d)
        {
            d.Age++;
            return d;
        }

        public string Name { get; set; }
        public int Age { get; set; }
        public string Nickname { get; set; }

        public Dog(string name, int age)
        {
            Name = name;
            Age = age;
            Nickname = "?";
        }

        public override string ToString()
        {
            return string.Format("{0}, Age {1}, Nickname [{2}]", Name, Age, Nickname);
        }
    }

We can use the operator as follows:

            Dog kirby = new Dog("Kirby", 10);
            kirby.Nickname = "Ball Chaser";
            Console.WriteLine(kirby);

            kirby++;
            Console.WriteLine(kirby);

1099-001

#1,075 – Operator Precedence Doesn’t Affect Operand Evaluation Order

Rules for operator precedence and associativity determine the order in which the operators within an expression will be evaluated.  Parentheses can also change the order in which the operators are evaluated.

For example:

            // * operator evaluated before +
            int sum1 = 1 + 2 * 3;    // 7

            // + operator evaluated first
            int sum2 = (1 + 2) * 3;  // 9

Operands, however, are evaluated from left-right, regardless of the order of evaluation of the operators. For example, in the code below, the sub-expression Return2() * Return3() is evaluated before adding Return1().  But console output shows us that Return1() was executed first.

        static int Return1()
        {
            Console.WriteLine("1");
            return 1;
        }

        static int Return2()
        {
            Console.WriteLine("2");
            return 2;
        }

        static int Return3()
        {
            Console.WriteLine("3");
            return 3;
        }

        static void Main(string[] args)
        {
            // * operator evaluated before +
            int sum1 = Return1() + Return2() * Return3();    // 7
        }

1075-001