HomeSearch

C# if Statement

Encode branching logic with if, else-if and else. Evaluate conditions to true or false.
If, else. Consider the present. From this point on, many things may happen. Lightning strikes the ground. An infinite array of possibilities exists.
With an if-statement, we test for a possibility. This statement detects if a condition (an expression) evaluates to true. A number equals 9. We can test for this.
First example. This program performs a division. Because the division is with constant numbers, this is computed at compile-time. It then tests it in an if-statement.

Step 1: The condition inside the if is evaluated to a boolean value. The value equals 5 and thus the expression evaluates to true.

Step 2: The inner block, with Console.WriteLine, is reached. The string representation of true, "True" is printed.

C# program that uses if-statement using System; class Program { static void Main() { int value = 10 / 2; // Step 1: test value against constant 5. if (value == 5) { // Step 2: if expression evaluated to true, run this code. Console.WriteLine(true); } } } Output True
True, false. We can specify, and test against, these Boolean literals. And an operator, the unary negation ("!") tests against false directly.True, False

Tip: A test like (value == 10) evaluates to true or false. We can make explicit the comparison against true if we want.

C# program that shows comparison against true using System; class Program { static void Main() { int value = 10; // Every expression in an if-statement must evaluate to true or false. // ... A comparison that is true evaluates to true. if (value == 10) { Console.WriteLine("TRUE"); } if ((value == 10) == true) { Console.WriteLine("TRUE"); } } } Output TRUE TRUE
Else if. What else is there? The else statement. Here, the Test method uses the if-statement with two else-if blocks and one else. The order of the if-statement tests is important.

So: We must test the more restrictive conditions first, or the less restrictive ones will match both cases.

Branches: In this program, the Test method uses several branching instructions expressed in high-level if-statements.

C# program that uses if, else using System; class Program { static void Main() { // Call method with embedded if-statement three times. int result1 = Test(0); int result2 = Test(50); int result3 = Test(-1); // Print results. Console.WriteLine(result1); Console.WriteLine(result2); Console.WriteLine(result3); } static int Test(int value) { if (value == 0) { return -1; } else if (value <= 10) { return 0; } else if (value <= 100) { return 1; } else { return 2; } } } Output -1 1 0
Else. A key question in code, computer science, and the universe is whether to use a return in an else-block. If we omit the else, we lose symmetry.

Method A: This version of the method uses a return statement at the end of an else-block.

Method B: This method has the same result as the method A, but omits the else-block.

Info: The 2 methods have equivalent performance and intermediate representations. This comes down to your style.

C# program that shows else constructs using System; class Program { static void Main() { Console.WriteLine(A(5)); // Call method A. Console.WriteLine(B(4)); // Call method B. } static bool A(int y) { if (y >= 5) { return true; } else { return false; } } static bool B(int y) { if (y >= 5) { return true; } return false; } } Output True False
Expressions. The expression in an if-statement must evaluate to true or false. A number result, or a reference type, is not accepted—a compiler error will occur. Expressions can be complex.

Tip: In this program, try changing the values of A and B to 1 and 2. With those values, the if-expressions both evaluate to false.

C# program that uses if, expressions using System; class Program { static void Main() { int a = 1; int b = 3; // Use negated expression. if (!(a == 1 && b == 2)) { Console.WriteLine(true); } // Use binary or version. if (a != 1 || b != 2) { Console.WriteLine(true); } } } Output True True
Brackets are not always required in C# programs. In this example, we use no curly brackets. The bodies of the if-statements are simply the following statements in the source.

Caution: This style is often a bad idea. You cannot add a second line to the body of one of these if-statements.

And: Visual Studio will now insert brackets automatically, so this style of code is not even faster to develop.

C# program that uses no brackets using System; class Program { static void Main() { int value = 1; int size = 2; if (value == 1) if (size == 2) Console.WriteLine("1, 2"); if (value == 0) Console.WriteLine("0"); // Not reached. else Console.WriteLine("Not 0"); // Reached. if (value == 2) Console.WriteLine("2"); // Not reached. } } Output 1, 2 Not 0
Nested. Nesting if-statements will create a similar flow of control to the boolean && operator. The arrangement of if-statements impacts performance.

Method1, Method2: These two methods are identical when compiled. No performance difference will exist.

Method3: This version inverts the order of the checks, so is faster when value > 100.

Note: Nesting ifs can help code clarity. If other statements need to be executed, nesting can also combine logic blocks.

C# program that uses nested ifs using System; class Program { static void Main() { Method1(50); Method2(50); Method3(50); } static void Method1(int value) { if (value >= 10) { if (value <= 100) { Console.WriteLine(true); } } } static void Method2(int value) { if (value >= 10 && value <= 100) { Console.WriteLine(true); } } static void Method3(int value) { if (value <= 100 && value >= 10) { Console.WriteLine(true); } } } Output True True True
Error, cannot convert. The C# compiler returns a compile-time error if we try to compile this program. A variable cannot be assigned within an if-statement. This protects us from typos.
C# program that causes error class Program { static void Main() { int i = 100; // This does not compile! if (i = 200) { System.Console.WriteLine("Zebra"); } } } Output Error CS0029 Cannot implicitly convert type 'int' to 'bool'
Warning, possible mistake. An empty statement is a valid statement to place in an else block. But this is more likely an error in programs—a misplaced semicolon.

Here: We have just a semicolon in the else block. This is valid, and the else clause would be considered empty.

But: This could be a typo, so the C# compiler helpfully warns us with the "possible mistaken empty statement" message.

Tip: Use an empty block with brackets if you wish to have an empty else-statement—no warning will be issued.

C# program that has possible mistaken empty statement class Program { static void Main() { int bird = 2; if (bird == 3) { } else ; } } Output Warning CS0642 Possible mistaken empty statement
Ternary. What is the ternary operator? It is like a question with two answers. It allows us to express a predicate and two consequent statements inside one statement.Ternary Operator

Example: We use the ternary operator in UseTernary(), and the if-else statements in UseIf.

Version 1: This code uses the ternary operator in UseTernary. If the argument is 5, it returns 10, otherwise it returns 20.

Version 2: This version use the if and else statements—the end result is the same as the ternary version.

Internals: The .NET Framework compiles the statements to the same branch statements in most cases (some slight differences can be present).

C# program that shows ternary statement using System; class Program { static void Main() { // Version 1: use ternary. Console.WriteLine("TERNARY: " + UseTernary(5)); Console.WriteLine("TERNARY: " + UseTernary(100)); // Version 2: use if. Console.WriteLine("IF: " + UseIf(5)); Console.WriteLine("IF: " + UseIf(100)); } static int UseTernary(int argument) { return argument == 5 ? 10 : 20; } static int UseIf(int argument) { if (argument == 5) { return 10; } else { return 20; } } } Output TERNARY: 10 TERNARY: 20 IF: 10 IF: 20
Null coalescing. This operator uses two question marks. Similar to ternary, it can only be used on a reference variable. It can reduce source code size.Null Coalescing

Tip: We can replace an if statement that tests against null, or a ternary statement, with a null coalescing expression.

C# program that uses null coalescing operator using System; class Program { static void Main() { // Use null coalescing instead of ternary or if-statements. string value = null; Console.WriteLine(value ?? "NO VALUE"); value = "bird"; Console.WriteLine(value ?? "NO VALUE"); } } Output NO VALUE bird
Benchmark, reorder. Suppose a value we want to test in a program is almost always 3. But 1% of the time it is not 3, so we still need to test for 2 and 1. We can test for 3 first.

Version 1: This version of the code tests for the value 3 last in a series of if-statements.

Version 2: This version tests for the value 3 first in the if-statements. It uses the reorder-if optimization.

Result: Testing for the value that is most commonly seen first (in this program, 3) is faster. This can help with chained expressions also.

Tip: When we put the most common matching if-statement first, it becomes faster, but all others become slower. But overall, this is a win.

C# program that times if-reordering optimization using System; using System.Diagnostics; class Program { const int _max = 1000000; static void Main() { // Version 1: if-statements in non-optimized order. var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { Method1(100, 3); } s1.Stop(); // Version 2: most common match first (reordered ifs). var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { Method2(100, 3); } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); } static int Method1(int max, int testValue) { // Test the argument in a loop. int result = 0; for (int i = 0; i < max; i++) { if (testValue == 1) { result++; } else if (testValue == 2) { result--; } else if (testValue == 3) { result += 2; } } return result; } static int Method2(int max, int testValue) { // Test the argument in a loop, but with most common if first. int result = 0; for (int i = 0; i < max; i++) { if (testValue == 3) { result += 2; } else if (testValue == 1) { result++; } else if (testValue == 2) { result--; } } return result; } } Output 149.84 ns Tests 1, 2, 3 (value is 3) 101.98 ns Reordered, tests 3, 1, 2 (value is 3)
Benchmark, table. A lookup table can completely replace an if-else chain. And often this will yield a speedup. A one-to-one mapping of test value to result is easiest to replace.

Version 1: This version of the code tests for 3 values and sets the result with if-else statements.

Version 2: This version uses a precomputed lookup table. It generates the same results as version 1.

Result: The mapping from test value to result value is the same in each version of the code. But the lookup table consistently is faster.

Tip: This optimization is easiest when each test value has a single result value. A dictionary can also be used as lookup table.

C# program that times lookup table optimization using System; using System.Diagnostics; class Program { const int _max = 10000000; static void Main() { int[] lookup = new int[] { 10, 50, 7 }; // Version 1: use if, else statements to get a single int result. var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { int test = i % 3; int result = 0; if (test == 0) { result = 10; } else if (test == 1) { result = 50; } else if (test == 2) { result = 7; } if (result == 0) { break; } } s1.Stop(); // Version 2: use lookup table instead of if to get a single int result. var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { int test = i % 3; int result = lookup[test]; if (result == 0) { break; } } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); } } Output 2.92 ns if/else if/else if chain 2.47 ns lookup[test]
If optimization, lookup. Large if-blocks have a lot of indirection and slow a program down. This can be improved with a Dictionary—we can encode the branches in data objects.

Switch: Sometimes a switch statement is faster than an if-statement. But this is a complex issue.

If vs. SwitchSwitch

Dictionary: A Dictionary can be used as a lookup table. This transforms complex if-statements into a single lookup.

Dictionary

Virtual dispatch: Use the type system to add behavior to objects. Place objects in a Dictionary and call their virtual methods.

Virtual
Intermediate language. If-statements are translated into machine code. The intermediate language is a step in this translation. High-level languages provide structured models.

However: These blocks have no meaning to the execution engine. They are instead translated into single instructions in IL.

And: The IL is flat. It is without scope. If-statements are translated to branch instructions.

IL: bne
Research, branches. If the code has a normal, expected path, try not to obscure that path with excessive if-statements. Instead, use ifs for branches from that path.

And: If 2 paths are possible, it is often better to put the common one in the if, and the uncommon one in the else.

Important: The more important (common) paths should come first. So we minimize the number of instructions.

Quote: Write your code so that the normal path through the code is clear. Make sure that the rare cases don't obscure the normal path of execution. This is important for both readability and performance (Code Complete).

A summary. In reading code, earlier things are perceived as more important. So put the high-value if-checks first. With the syntax for if-statements, we direct the flow of control.
Home
Dot Net Perls
© 2007-2020 Sam Allen. Every person is special and unique. Send bug reports to info@dotnetperls.com.