The C# Definitive Guide

The C# Definitive Guide

Are you looking for a learning path to be “fluent” in C#? This is the right place for you! This is my definitive guide to what every beginner and intermediate C# developer should know.

Every intermediate C# developer should know how to productively work with Visual Studio or Visual Studio Code, use async/await keywords, most common LINQ methods and regular expressions. Also, to get around large codebases and be aware of the latest C# features.

Environment

Visual Studio is the de-facto Integrated Development Environment (IDE) for C#. Since we will spend most of our workdays with Visual Studio, we should setup Visual Studio to make use more productive.

For more settings and extensions, check my Visual Studio setup for C#.

The C# Definitive Guide
Have everything ready to level up your C#. Photo by Andrew Neel on Unsplash

Git and Github

Git

Git is a version control system. A time machine to go back in time, create alternate stories from a point in time and make alternate stories join your present. You got the analogy?

If you are creating a zip file with your code and naming it after the date of your latest change, Git is a better way.

GitHub

Programming is about collaboration. GitHub is the social network for programmers.

With GitHub, you can show your own code, ask for new features in a library, report bugs in the software you use, read and make questions about someone else’s code.

Microsoft, Facebook, Google have some of their own code available on GitHub.

Design Patterns and Object-Oriented Design Principles

Desing patterns are recipes to solve common problems in code. This is, given a certain problem, there is a blueprint or an outline that will help you to solve that problem.

Dealing with large codebases

Programming is also about reading code. Get used to navigate throught large codebases.

For more guidelines about reading code, check Changelog One sure-fire way to improve your coding.

Unit tests

A unit test is a “safety net” to make sure you don’t break things when you add new features or modify your codebase. A unit test is a piece of code that uses your code base from a “user” point of view and verifies a given behavior.

LINQ

Language-Integrated Query, LINQ, is the declarative way to work with collections in C# or anything that looks like one. Instead of writing the looping and the business logic with foreach, for or while, you should try to do it with LINQ.

For example, given a string of emails separated by commas, the next lines show how to create an array with the trimmed emails using LINQ.

string emails = "email1@yourdomain.com,    email2@yourdomain.com,email2@yourdomain.com";
string[] trimmed = emails.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                         .Select(t => t.Trim())
                         .ToArray();

Regular Expressions

Have you ever used *.txt in the file explorer to find all text files in a folder? If so, you have already used a regular expression. But, *.txt is just the tip of the iceberg.

Regular expressions give you a search syntax to find patterns of text in a string. For example, find all phone numbers like this one (+57) 3XX XXX-XXX, you could use (\(\+\d{2}\))\s(\d{3})\s(\d{3})\-(\d{3}).

async/await

Asyncronous code is code that doesn’t block when executing long-running operations.

Some new features in C# since version 6

C# is an evolving language. With every new version, we have more features to write more concise code. These are some of the new features in C# we use more often.

C# 6.0

String interpolation

Before we wrote,

string.Format("Hello, {0}", name);

Now we can write,

$"Hello, {name}";

Null-conditional operators

There are two new operators to check for null values: ?? and ?..

Before,

string name = ReadNameFromSomewhere();
if (name == null)
    name = "none";
else
    name.Trim();

After,

string name = ReadNameFromSomewhere();
name?.Trim() ?? "none"

C# 7.X

Inlined out variables

Now, we can inline the variable declaration next to the out keyword.

Before,

int count = 0;
int.TryParse(readFromKey, out count);

After,

int.TryParse(readFromKey, out var count)

Or even,

int.TryParse(readFromKey, out _)

Reference Tuple Members by Name

We can access values inside tuples by name, instead of Item1 or Item2.

Before,

Tuple<string, string> Greet() { }

var greeting = Greet()
var name = greeting.Item1;

After,

(string Salutation, string Name) Greet() { }

var greeting = Greet()
greeting.Name

C# 8.0

Null-coalescing assignment

Now, there is the operator ??= to only assign a variable if its value isn’t null.

Before,

int? magicNumber = null;

if (magicNumber == null)
    magicNumber = 7;

After,

int? magicNumber = null;

magicNumber ??= 7;

Using declarations

A variable preceded by using is disposed at the end of the scope.

Before,

using (var reader = new StreamReader(fileName))
{
    string line; 
    while ((line = reader.ReadLine()) != null)  
    {  
        // Do something  
    }  
}

After,

using var reader = new StreamReader(fileName);

string line; 
while ((line = reader.ReadLine()) != null)  
{  
    // Do something  
}

Nullable reference types

All reference variables are non-nullable by default. Any attempt to dereference a nullable reference gets a warning from the compiler. Goodbye, NullReferenceException!. Be aware, you need to turn on this feature at the project level. Add <Nullable>enable</Nullable> inside the PropertyGroup in your csproj files.

Before,

int notNull = null; // <- error CS0037: Cannot convert null to 'int'
int? canBeNull = null;

string name = null;
SayHi(name) // <- System.NullReferenceException

void SayHi(string name) => Console.WriteLine(name.Trim()); 

After,

string name = null;
// ^^^^^
// warning CS8600: Converting null literal or possible null value to non-nullable type.

string? canBeNullName = null;
SayHi(name);
// ^^^^^
// warning CS8604: Possible null reference argument for parameter 'name'

C# 9.0

Records

A record is an immutable reference type with built-in equality methods. When you create a record, the compiler creates a ToString method, a value-based equality methods and other methods for you. Records are helpful to replace value-objects in your code.

public record Movie
{
    public string Title { get; }
    public int ReleaseYear { get; }
    
    public Movie(string title, int releaseYear) => (Title, ReleaseYear) = (title, releaseYear);
}

Top-level statements

All the boilerplate is now gone from Main methods.

Before,

using System;

namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

After,

using System;

Console.WriteLine("Hello World!");

To learn about other C# features, check my top 10 or so best C# features.

Bonus Points

Voilà! That’s my take on what every intermediate C# developer should know! Don’t be overwhelm by the amount of things to learn. Don’t try to learn everything at once, either. Learn one subject at a time! And, start using it in your every day coding as you learn it.

Happy coding!