class: center, middle # Some best practices in CSharp
--- ## Agenda 1. **General** * Names * Variables * Functions * Comments 2. **CSharp** * Strings * LINQ * Web.Api * Async/Await 3. **New features in CSharp** --- # General --- ## Names ### Before ```csharp Student stu = new Student(); var stuObj = new Student(); var txtName = "Alice"; var namesList = new string[]{ "Alice", "Bob" }; ``` ### After ```csharp Student s = new Student(); var s = new Student(); var name = "Alice"; var names = new string[]{ "Alice", "Bob" }; ``` --- ## Boolean variables ### Before ```csharp public void Function() { int sw = 0; // ... if (sw == 1) { // Something wrong happened } } ``` --- ## Boolean variables ### After ```csharp public void Function() { bool sw = false; //... if (sw) { } } ``` ### Even Better * Use an interrogative variable name: `isAvailable`, `hasError` * Use affirmative names: `isAsync` vs `isNotSync` --- ## Boolean variables 2 ### Before ```csharp var aBool = true; if (aBool == true) { // ... } ``` ### After ```csharp var aBool = true; if (aBool) { // ... } ``` --- ## Boolean variables 3 ### Before ```csharp return someCondition ? true : false; ``` ```csharp if (someCondition) { return true; } else { return false; } ``` ### After ```csharp return someCondition; ``` --- ## Variable declaration * Declare variables before their first use ### Before ```csharp public void DoSomething() { int a, b, c; // blah, blah, blah // 100 lines of code later if (a > 1) DoSomething(); // 100 lines of code more if (b != 0) DoSomethingElse(); } ``` --- ## Variable declaration ### After ```csharp public void DoSomething() { int a = 0; // Some action to update a if (a > 1) DoSomething(); } ``` --- ## Size of functions * A function should do only one thing. To know it, try to describe what your function does * Keep your function as small as possible, for example _the size of a screen_ * **Extract method**: Extract some portions of your function into another one with a descriptive name --- ## Indentation level > _... The answer to that is that if you need more than 3 levels of indentation, you're screwed anyway, and should fix your program._ ### Before ```csharp public void MyFunction() { foreach (var e in collection) { if (something) { if (somethingElse) { if (somethingElseAgain) { //... } } } } } ``` --- ## Indentation level ### After ```csharp public void MyFunction() { foreach (var e in collection) { if (!something) break; // ... } } ``` ### Even better ```csharp public void MyFunction() { foreach (var e in collection) { DoSomenthing(e); } } ``` --- ## Functions side effects * Any unexpected action from our functions. Avoid any side effects ### Before ```csharp public int Add(int a, int b) { int sum = a + b; Database.Store(sum); return sum; } ``` --- ## Functions side effects * In case you can't avoid them, at least make them explicit ### After ```csharp public int AddAndStoreSum(int a, int b) { int sum = a + b; Database.Store(sum); return sum; } ``` --- ## Functions side effects * Let your function caller deal with the effect ### Even better ```csharp public int Add(int a, int b) { int sum = a + b; return sum; } var sum = Add(1, 2); Database.Store(sum); ``` --- ## Comments * Don't comment obvious things ```csharp // Increment by 1 i++; // Create a class class JustAClass { } ``` * Don't comment out code * Is it a work-in-progress? * Is it dead code? * Use a control version system, instead --- ## CSharp --- ## Print null strings ### Before ```csharp str.ToString() ``` ### After ```csharp $"{str}" ``` --- ## Separate elements by comma ### Before ```csharp string str = ""; foreach (var e in array) str += e + ","; str = str.Substring(str.Length - 1); ``` ### After ```csharp string.Join(",", array) ``` --- ## Aside: LINQ Linq is the declarative way for C# to filter, order, group and in general, operate with or "query" collections or anything that looks like one. **Example**: Create a list of odd integers between two integers. `OddsBetween(2, 7) == [3,5,7]` ```csharp public static List
OddsBetween(int l, int r) { var odds = new List
(); for (int i = l; i <= r; i++) if (i % 2 != 0) odds.Add(i); return odds; } ``` ```csharp public static List
OddsBetween(int l, int r) { return Enumerable.Range(l, r - l + 1) .Where(i => i % 2 != 0) .ToList(); } ``` --- ## Looping with index ### Before ```csharp int i = 0; foreach (var element in array) { DoSomething(i, element); i++; } ``` ### After ```csharp foreach (var element in array.Select((Value, Index) => new { Index, Value })) { DoSomething(element.Index, element.Value); } ``` --- ## Where vs FirstOrDefault ### Before ```csharp array.Where(condition).FirstOrDefault(); ``` ### After ```csharp array.FirstOrDefault(condition); ``` --- ## FirstOrDefault with no elements ### Before ```csharp var element = array.FirstOrDefault(condition); element.Something(); ``` ### After ```csharp var element = array.FirstOrDefault(condition); element?.Something(); ``` --- ## Count vs Any ### Before ```csharp if (collection.Count() == 0) { // Do something } ``` ### After ```csharp if (collection.Any()) { // Do something } ``` --- ## Where vs Any ### Before ```csharp array.Where(condition).Any(); ``` ### After ```csharp array.Any(condition); ``` --- ## Do not use dynamic for view models and dtos ### Before ```csharp public HttpResponseMessage DoSomething(dynamic viewModel) { } ``` ### After ```csharp public HttpResponseMessage DoSomething(SomethingViewModel viewModel) { } ``` --- ## aside: Aysncronous programming * Asyncronous code doesn't block when executing long-running operations, like network request or I/O operations * *An analogy*: A waitress at a cafe doesn't wait for the coffee machine to finish. She is available to continue doing other things. But, the coffee machine has a mechanism to let her know when the coffee is ready so she can complete the order. * An async method: * has **`async`** in the declaration and return **`Task`** or **`Task
`** * includes, by convention, _"Async"_ as suffix in the name * uses **`await`** keyword in the body --- ## end of aside
[task-asynchronous-programming-model](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model) --- ## Async all the way down `Task.Wait()` or `Task.Result` or `Task.GetAwaiter().GetResult()` are troublesome ### Before ```csharp public async Task
DoSomething() { var something = DoSomethingAsync().Result; } ``` ### After ```csharp public async Task
DoSomething() { var something = await DoSomethingAsync(); } ``` [dont-block-on-async-code](https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html) --- ## Do not do async void ### Before ```csharp public async void DoSomethingAsync() { } ``` ### After ```csharp public async Task DoSomethingAsync() { } ``` --- ## Some new features in C# since version 6 These are one of the new features in C# since version 6.0 you can use more often. * Before `string.Format("Hello, {0}", name)`, after `$"Hello, {name}"` * Before `int count = 0; int.TryParse(readFromKey, out count)`, after `int.TryParse(readFromKey, out var count)` * Before, ```csharp string name = ReadNameFromSomewhere(); if (name == null) name = "none"; else name.Trim(); ``` After, ```csharp string name = ReadNameFromSomewhere(); name?.Trim() ?? "none"; ``` --- * Now, one-line functions are truly one liners, `public int OneLineFunction() => 0;` * Before, ```csharp Tuple
Greet() { } var greeting = Greet(); var name = greeting.Item1; ``` After, ```csharp (string Salutation, string Name) Greet() { } var greeting = Greet(); greeting.Name; ``` * Now, `async` Main method in Console apps is available ```csharp public static async Task Main(string[] args) { await DoSomethingAsync(); } ``` --- * Static imports ```csharp import static System.Console; static void Main() { WriteLine("Hello, world!"); } ``` * `nameof` operator ```csharp public void Method(string param1) { if (string.IsNullOrEmpty(param1)) throw new ArgumentNullException(nameof(param1)); } ``` * Read-only references: `in` keyword ```csharp public string Greet(in string name) { // error CS8331: Cannot assign to variable 'in string' because it is a readonly variable //name = null; } ``` --- class: center, middle # That's all folks!