DEV Community

Dave Brock
Dave Brock

Posted on • Originally published at daveabrock.com on

Reduce mental energy with C# 9

This is a humbling yet completely accurate fact: you spend much more time reading code than writing it. Any experienced programmer will tell you the reading-to-writing ratio is easily 5-to-1 or even 10-to-1. You’re understanding how things work. You’re hunting for bugs. You’re scrolling past code with thoughts like, “Nope, doesn’t apply … doesn’t matter, doesn’t matter …” until you have to pause and think, and spend a silly amount of time trying to understand how something works.

It could be a developer trying to be clever, or an unfortunate function with an arrow-shaped pattern … you know, a variety of things. Whatever the case, it interrupts your flow. When you think how much time you spend reviewing code, it adds up and can turn into a big annoyance.

For example, let’s say you’re trying to figure out a bug and you come across this C# 8 code.

if (!(dave is Developer))  
Enter fullscreen mode Exit fullscreen mode

This is getting a little ridiculous. Nobody has time for this negation logic and double parentheses. Best case, it interrupts your flow and mental model. Worst case, you scan it and misunderstand it. I might sound crazy, I get it - this may have only taken an extra few seconds. But for a large application, hundreds of times a day? You see what I mean? Why couldn’t I do something like this?

if (dave is not Developer)
Enter fullscreen mode Exit fullscreen mode

See? I completely understand this: I can keep scrolling or stop and know I’ve found my bug. If only I could do this, you think.

If you aren’t aware, you can. This syntax, and other improvements, are available in the upcoming C# 9 release, slated to be released with .NET 5 in November 2020. C# 9 has a lot, but this post is going to focus on improvements that help restore valuable mental energy that is required in a mentally exhausting profession. And before you ask: no, C# 9 isn’t full of FDA-approved health benefits, but I’ve found some great stuff that helps make code cleaner, more maintainable, and easier to understand, and prevents a lot of “wait, what?” moments.

Let’s take a look at what’s coming. This is just scratching the surface, and I’ll write about more features in-depth as I come across them. I think you’ll find the more you dive into C# 9, the more you appreciate its adoption of the functional programming, “no side effects” model.

Records

One of the biggest features coming out of C# 9 is the concept of records. Records allow an entire object to be immutable, meaning you can do value-like things on them. Think data, not objects.

Heads up! The C# 9 Microsoft article describes records with the data keyword. That has been replaced with record.

Let’s take a Developer record:

public record Developer
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
    public string PreferredLanguage { get; init; }
}
Enter fullscreen mode Exit fullscreen mode

Wait, what is init doing there? That is an init-only property, also new to C# 9. Before this, your properties needed to be mutable for them to be initialized. With init accessors, it’s like set except it can only be called during object initialization.

Anyway, our record now gives us access to some other cool stuff that makes for some clean code.

Data member simplification

With C# 9, we can initialize our Developer record this way instead:

public record Developer { string FirstName; string LastName; string PreferredLanguage; }
Enter fullscreen mode Exit fullscreen mode

With-expressions

Much of your data is immutable, so if you wanted to create a new object with much, but not all, of the same data, you would do something like this (your use cases would be much more complicated, hopefully) are probably used to doing something like this in regular C# 8 with classes.

using System;

var developer1 = new Developer
{
    FirstName = "David",
    LastName = "Brock",
    PreferredLanguage = "C#"
};

// ...
var developer2 = developer1;
developer2.LastName = "Pine";
Console.WriteLine(person2.FirstName); // David
Console.WriteLine(person2.LastName); // Pine
Console.WriteLine(person2.PreferredLanguage); // C#

public class Developer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string PreferredLanguage { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

In C# 9, try a with expression instead, with your records:

using System;

var developer1 = new Developer
{
    FirstName = "David",
    LastName = "Brock",
    PreferredLanguage = "C#"
};

var developer2 = developer1 with { LastName = "Pine" };
Console.WriteLine(developer2.FirstName); // David
Console.WriteLine(developer2.LastName); // Pine
Console.WriteLine(developer2.PreferredLanguage); // C#

public record Developer
{
    public string FirstName;
    public string LastName;
    public string PreferredLanguage;
}
Enter fullscreen mode Exit fullscreen mode

You can even specify multiple properties to just include what you need changed.

This C# 9 example above is actually an example of a top-level program! Speaking of which…

Top-level programs

This is my favorite, even if I don’t write a lot of console applications. Inside your Main method you would typically see:

using System;

public class MyProgram
{
    public static void Main()
    {
        Console.WriteLine("Hello, Wisconsin!");
    }
}
Enter fullscreen mode Exit fullscreen mode

No more of this silly boilerplate code! After your using statements, do this:

using System;

Console.WriteLine("Hello, Wisconsin!");
Enter fullscreen mode Exit fullscreen mode

This will need to follow the Highlander rule - there can only be one - but the same argument applies to the Main() entry method in your console applications today.

Logical patterns

OK, moving on from records (for now). With the is not pattern we used to kick off this post, we showcased some logical pattern improvements. You can officially combine any operators with and, or, and not.

A great use case would be for every developer’s battle: null checking. For example, you can easier code against null, or in this case, not null:

not null => throw new ArgumentException($"Not sure what this is: {yourArgument}", nameof(yourArgument))
Enter fullscreen mode Exit fullscreen mode

New expressions for target types

Let’s say I had a Developer type that takes in a first and last name from a constructor. To create the object, I’d do something like this:

Developer dave = new Developer("Dave", "Brock", "C#");
var dave = new Developer("Dave", "Brock", "C#");
Enter fullscreen mode Exit fullscreen mode

With C# 9, you can leave out the type.

Developer dave = new ("Dave", "Brock", "C#");
Enter fullscreen mode Exit fullscreen mode

Playing with the C# 9 preview bits

Are you reading this before the C# 9 release in November 2020? If you want to play with the C# 9 bits, some good news: you can use the LinqPad tool to do so with a click of a checkbox - no install required!

Latest comments (0)