An if
-statement tests for a possibility in C# programs. This statement (alongside "else") detects if an expression like "x == 10" evaluates to true.
We can improve the performance of evaluating if
-statements by placing the most common case first. This may also help readability.
Here we consider the parts of an if-else
statement. Test()
uses the if
-statement with two else
-if blocks and one else. The order of the if
-statement tests is important.
Test()
. This value is not zero, but it is less than or equal to 10, so 0 is returned.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; } } }-1 1 0
We consider whether we should use a return in an else
-block. Some code styles emphasize symmetry, and so the else is preferred.
else
-block.else
-block.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; } }True False
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.
if
-expressions both evaluate to false.using System; 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); }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 single statements in the source.
using System; 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.1, 2 Not 0
Nesting if
-statements will create a similar flow of control to the boolean "and" operator. The arrangement of if
-statements impacts performance.
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); } } }True True True
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.
class Program { static void Main() { int i = 100; // This does not compile! if (i = 200) { System.Console.WriteLine("Zebra"); } } }Error CS0029 Cannot implicitly convert type 'int' to 'bool'
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.
else
-statement—no warning will be issued.class Program { static void Main() { int bird = 2; if (bird == 3) { } else ; } }Warning CS0642 Possible mistaken empty statement
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.
UseTernary()
, and the if-else
statements in UseIf
.UseTernary
. If the argument is 5, it returns 10, otherwise it returns 20.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; } } }TERNARY: 10 TERNARY: 20 IF: 10 IF: 20
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
, or a ternary statement, with a null
coalescing expression.using System;
// Use null coalescing instead of ternary or if-statements.
string value = null;
Console.WriteLine(value ?? "NO VALUE");
value = "bird";
Console.WriteLine(value ?? "NO VALUE");NO VALUE
bird
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.
if
-statements.if
-statements. It uses the reorder-if optimization.using System; using System.Diagnostics; class Program { 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; } 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")); } }155.96 ns Tests 1, 2, 3 (value is 3) 113.10 ns Reordered, tests 3, 1, 2 (value is 3)
A lookup table can completely replace an if-else
chain. And often this will yield a speedup. A one-to-one mapping oftest value to result is easiest to replace.
if-else
statements.using System; using System.Diagnostics; const int _max = 100000000; 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"));1.86 ns if/else if/else if chain 1.53 ns lookup[test]
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.