#!markdown # Chapter 3 - Controlling Flow and Converting Types #!markdown The following code cell statically imports the Console class so that we can call the `WriteLine` method anywhere. #!csharp using static System.Console; #!markdown # Operating on variables #!markdown ## Exploring unary operators #!csharp int a = 3; int b = a++; WriteLine($"a is {a}, b is {b}"); int c = 3; int d = ++c; // increment c before assigning it WriteLine($"c is {c}, d is {d}"); #!markdown ## Exploring binary arithmetic operators #!csharp int e = 11; int f = 3; WriteLine($"e is {e}, f is {f}"); WriteLine($"e + f = {e + f}"); WriteLine($"e - f = {e - f}"); WriteLine($"e * f = {e * f}"); WriteLine($"e / f = {e / f}"); WriteLine($"e % f = {e % f}"); double g = 11.0; WriteLine($"g is {g:N1}, f is {f}"); WriteLine($"g / f = {g / f}"); #!markdown ## Exploring logical operators #!csharp bool a = true; bool b = false; WriteLine($"AND | a | b "); WriteLine($"a | {a & a,-5} | {a & b,-5} "); WriteLine($"b | {b & a,-5} | {b & b,-5} "); WriteLine(); WriteLine($"OR | a | b "); WriteLine($"a | {a | a,-5} | {a | b,-5} "); WriteLine($"b | {b | a,-5} | {b | b,-5} "); WriteLine(); WriteLine($"XOR | a | b "); WriteLine($"a | {a ^ a,-5} | {a ^ b,-5} "); WriteLine($"b | {b ^ a,-5} | {b ^ b,-5} "); #!markdown ## Exploring conditional logical operators #!csharp private static bool DoStuff() { WriteLine("I am doing some stuff."); return true; } #!csharp WriteLine($"a & DoStuff() = {a & DoStuff()}"); WriteLine($"b & DoStuff() = {b & DoStuff()}"); #!csharp WriteLine($"a && DoStuff() = {a && DoStuff()}"); WriteLine($"b && DoStuff() = {b && DoStuff()}"); #!markdown ## Exploring bitwise and binary shift operators #!csharp int a = 10; // 0000 1010 int b = 6; // 0000 0110 WriteLine($"a = {a}"); WriteLine($"b = {b}"); WriteLine($"a & b = {a & b}"); // 2-bit column only WriteLine($"a | b = {a | b}"); // 8, 4, and 2-bit columns WriteLine($"a ^ b = {a ^ b}"); // 8 and 4-bit columns #!markdown ## Miscellaneous operators #!csharp int age = 47; // How many operators in the following statement? char firstDigit = age.ToString()[0]; // There are four operators: // = is the assignment operator // . is the member access operator // () is the invocation operator // [] is the indexer access operator #!markdown # Understanding selection statements #!markdown ## Branching with the if statement #!csharp string password = "ninja"; if (password.Length < 8) { WriteLine("Your password is too short. Use at least 8 characters."); } else { WriteLine("Your password is strong."); } #!markdown ## Pattern matching with the if statement #!csharp // add and remove the "" to change the behavior object o = 3; int j = 4; if (o is int i) { WriteLine($"{i} x {j} = {i * j}"); } else { WriteLine("o is not an int so it cannot multiply!"); } #!markdown ## Branching with the switch statement #!csharp int number = (new Random()).Next(1, 7); WriteLine($"My random number is {number}"); switch (number) { case 1: WriteLine("One"); break; // jumps to end of switch statement case 2: WriteLine("Two"); goto case 1; case 3: // multiple case section case 4: WriteLine("Three or four"); goto case 1; case 5: // go to sleep for half a second System.Threading.Thread.Sleep(500); goto A_label; default: WriteLine("Default"); break; } // end of switch statement WriteLine("After end of switch"); A_label: WriteLine($"After A_label"); #!markdown ## Pattern matching with the switch statement #!csharp using System.IO; #!markdown `ReadKey` is not supported in .NET Interactive Notebooks. To simulate the user pressing R or W, the following code instantiates a ConsoleKeyInfo object that represents the R or W key being pressed. #!markdown Comment and uncomment the appropriate path depending on your OS. #!csharp //string path = "/Users/markjprice/Code/Chapter03"; string path = @"C:\Code\Chapter03"; Write("Press R for read-only or W for writeable: "); ConsoleKeyInfo key = // ReadKey(); new ConsoleKeyInfo('R', ConsoleKey.R, shift: false, alt: false, control: false); WriteLine(); Stream s = null; if (key.Key == ConsoleKey.R) { s = File.Open( Path.Combine(path, "file.txt"), FileMode.OpenOrCreate, FileAccess.Read); } else { s = File.Open( Path.Combine(path, "file.txt"), FileMode.OpenOrCreate, FileAccess.Write); } string message = string.Empty; switch (s) { case FileStream writeableFile when s.CanWrite: message = "The stream is a file that I can write to."; break; case FileStream readOnlyFile: message = "The stream is a read-only file."; break; case MemoryStream ms: message = "The stream is a memory address."; break; default: // always evaluated last despite its current position message = "The stream is some other type."; break; case null: message = "The stream is null."; break; } WriteLine(message); #!markdown ## Simplifying switch statements with switch expressions #!csharp message = s switch { FileStream writeableFile when s.CanWrite => "The stream is a file that I can write to.", FileStream readOnlyFile => "The stream is a read-only file.", MemoryStream ms => "The stream is a memory address.", null => "The stream is null.", _ => "The stream is some other type." }; WriteLine(message); #!markdown # Understanding iteration statements #!markdown ## Looping with the while statement #!csharp int x = 0; while (x < 10) { WriteLine(x); x++; } #!markdown ## Looping with the do statement #!csharp string password = string.Empty; int attempts = 0; do { if (attempts == 10) { break; } Write("Enter your password: "); password = "Pa$$w0rd"; // ReadLine(); attempts++; } while (password != "Pa$$w0rd"); if (attempts == 10) { WriteLine("You failed after 10 attempts!"); } else { WriteLine("Correct!"); } #!markdown ## Looping with the for statement #!csharp for (int y = 1; y <= 10; y++) { WriteLine(y); } #!markdown ## Looping with the foreach statement #!csharp string[] names = { "Adam", "Barry", "Charlie" }; foreach (string name in names) { WriteLine($"{name} has {name.Length} characters."); } #!markdown # Casting and converting between types #!csharp using static System.Console; using static System.Convert; #!markdown ## Casting numbers implicitly and explicitly #!csharp int a = 10; double b = a; // an int can be safely cast into a double WriteLine(b); double c = 9.8; int d = (int)c; // compiler gives an error for this line before adding (int) WriteLine(d); // d is 9 losing the .8 part due to the (int) cast above long e = 10; int f = (int)e; WriteLine($"e is {e:N0} and f is {f:N0}"); e = 5_000_000_000; // long.MaxValue; f = (int)e; WriteLine($"e is {e:N0} and f is {f:N0}"); #!markdown ## Converting with the System.Convert type #!csharp double g = 9.8; int h = ToInt32(g); // a method of System.Convert WriteLine($"g is {g} and h is {h}"); #!markdown ## Understanding the default rounding rules #!csharp double[] doubles = new[] { 9.49, 9.5, 9.51, 10.49, 10.5, 10.51 }; foreach (double n in doubles) { WriteLine($"ToInt32({n}) is {ToInt32(n)}"); } #!markdown ## Taking control of rounding rules #!csharp foreach (double n in doubles) { WriteLine(format: "Math.Round({0}, 0, MidpointRounding.AwayFromZero) is {1}", arg0: n, arg1: Math.Round(value: n, digits: 0, mode: MidpointRounding.AwayFromZero)); } #!markdown ## Converting from any type to a string #!csharp int number = 12; WriteLine(number.ToString()); bool boolean = true; WriteLine(boolean.ToString()); DateTime now = DateTime.Now; WriteLine(now.ToString()); object me = new object(); WriteLine(me.ToString()); #!markdown ## Converting from a binary object to a string #!csharp // allocate array of 128 bytes byte[] binaryObject = new byte[128]; // populate array with random bytes (new Random()).NextBytes(binaryObject); WriteLine("Binary Object as bytes:"); for(int index = 0; index < binaryObject.Length; index++) { Write($"{binaryObject[index]:X} "); } WriteLine(); // convert to Base64 string and output as text string encoded = ToBase64String(binaryObject); WriteLine($"Binary Object as Base64: {encoded}"); #!markdown ## Parsing from strings to numbers or dates and times #!csharp int age = int.Parse("27"); DateTime birthday = DateTime.Parse("4 July 1980"); WriteLine($"I was born {age} years ago."); WriteLine($"My birthday is {birthday}."); WriteLine($"My birthday is {birthday:D}."); #!markdown ## Errors using Parse #!csharp int count = int.Parse("abc"); #!markdown ## Avoiding exceptions using the TryParse method #!csharp Write("How many eggs are there? "); string input = "12"; // change to "twelve" if (int.TryParse(input, out int count)) { WriteLine($"There are {count} eggs."); } else { WriteLine("I could not parse the input."); } #!markdown ## Wrapping error-prone code in a try block #!csharp WriteLine("Before parsing"); Write("What is your age? "); string input = "49"; try { int age = int.Parse(input); WriteLine($"You are {age} years old."); } catch (OverflowException) { WriteLine("Your age is a valid number format but it is either too big or small."); } catch (FormatException) { WriteLine("The age you entered is not a valid number format."); } catch (Exception ex) { WriteLine($"{ex.GetType()} says {ex.Message}"); } WriteLine("After parsing"); #!markdown ## Throwing overflow exceptions with the checked statement #!csharp try { checked { int x = int.MaxValue - 1; WriteLine($"Initial value: {x}"); x++; WriteLine($"After incrementing: {x}"); x++; WriteLine($"After incrementing: {x}"); x++; WriteLine($"After incrementing: {x}"); } } catch (OverflowException) { WriteLine("The code overflowed but I caught the exception."); } #!markdown ## Disabling compiler overflow checks with the unchecked statement #!csharp int y = int.MaxValue + 1; #!csharp unchecked { int y = int.MaxValue + 1; WriteLine($"Initial value: {y}"); y--; WriteLine($"After decrementing: {y}"); y--; WriteLine($"After decrementing: {y}"); }