DEV Community

loading...
Cover image for C# 9 Language Highlights

C# 9 Language Highlights

Mohamad Lawand
Code is Life. Crossfitter. Solution Architect. Just another human walking the earth! https://youtube.com/c/mohamadlawand
・7 min read

In this article we will be going through some of the C# 9 language highlights.

So what we will cover today:

  • Target Typed new expressions
  • Property Pattern Matching
  • Tuple Pattern Matching
  • Init Only Setters
  • Default Interface Methods

You can watch the full video on Youtube

And you can find the source code on GitHub
https://github.com/mohamadlawand087/v30-CsharpHighlights

As always you will find the source code in the description down below. Please like, share and subscribe if you like the video. It will really help the channel

Ingredients

We will need to have 2 things installed on our machine

  • VS code
  • Dotnet 5 SDK

Links available on the screen and in the description down below

First we want to check the SDK version

dotnet --version
Enter fullscreen mode Exit fullscreen mode

Create a console application

dotnet new console -n CSharpHighlights
Enter fullscreen mode Exit fullscreen mode

Target Typed new expressions

This feature will allow me to express what i want to do in my code with less code to write.

In C# we typically create new instance of types with something like this

Car car1 = new Car("Toyota", "Black");
Enter fullscreen mode Exit fullscreen mode

Then the var keyword got introduced, var implicitly detected the type of the instance i am trying to create based on whats on the right hand side

var car2 = new Car("Toyota", "White");
Enter fullscreen mode Exit fullscreen mode

Now in C# 9 i can make the initialisation even shorter by doing the following, the new expression will figure out what i am trying to create based on whats on the left hand side

Car car3 = new ("Toyota", "Blue");
Enter fullscreen mode Exit fullscreen mode

Now let us see how this will help when trying to create a list, le say i want to create a list of Cars

Car[] cars = {
    new Car("Toyota","Red"),
    new Car("Jeep","White"),
    new Car("Volvo","Grey"),
    new Car("BMW","Yellow"),
    new Car("Mercedes","Black"),
    new Car("Honda","Silver")
};
Enter fullscreen mode Exit fullscreen mode

you can see from the list initialisation how many times i had to write the word Car to initiate a new object, its a lot of typing but with the use of the updated new expression it can be something like this

Car[] cars = {
    new ("Toyota","Red"),
    new ("Jeep","White"),
    new ("Volvo","Grey"),
    new ("BMW","Yellow"),
    new ("Mercedes","Black"),
    new ("Honda","Silver")
};
Enter fullscreen mode Exit fullscreen mode

And you can see my code is neater and it is still doing what i am expecting it do which is to create a list. So now if i want to to iterate through my list

foreach(var c in cars)
{
    Console.Write($"The Car type is {c.brand} of color {c.color} \n");
}
Enter fullscreen mode Exit fullscreen mode

i would still get the same output.

Property Pattern Matching

So in this example we will cover Property Pattern Matching specifically with switch expression. Using the record and the list we have created earlier the Car we will be adding a new method which will utilise PPM

decimal CalculateTax(Car car, decimal salesPrice) =>
            car switch
            {
                {brand: "Toyota"} => salesPrice * 0.02m,
                {brand: "Jeep"} => salesPrice * 0.023m,
                {brand: "Volvo"} => salesPrice * 0.029m,
                {brand: "BMW"} => salesPrice * 0.03m,
                {brand: "Mercedes"} => salesPrice * 0.034m,
                {brand: "Honda"} => salesPrice * 0.024m,

                _ => 0m
            };
Enter fullscreen mode Exit fullscreen mode

This is a switch expression on the brand itself so we evaluating the Car instance for each of the case arms we are evaluating individual properties to calculate the tax

In summary:

  • we are passing an instance of Car
  • this feature is allowing us to inspect any of the properties we have, in our case the brand
  • base on the value of the brand we are getting the tax

Now lets say we want to match on more then 1 property, so achieve this we need to do a compound property expression where the order does matter and takes precedents

decimal CalculateTax(Car car, decimal salesPrice) =>
            car switch
            {
                {brand: "Toyota"} => salesPrice * 0.02m,
                {brand: "Jeep"} => salesPrice * 0.023m,
                {brand: "Volvo"} => salesPrice * 0.029m,
                {brand: "BMW"} => salesPrice * 0.03m,
                                {brand: "Mercedes", color: "Black"} => salesPrice * 0.037m,
                                {brand: "Mercedes"} => salesPrice * 0.034m,
                {brand: "Honda"} => salesPrice * 0.024m,

                _ => 0m
            };
Enter fullscreen mode Exit fullscreen mode

Tuple Pattern Matching

We are going to discover what is tuple matching in the context of switch expression, we will create a function for rock, paper, scissors and we express this function using tuple that we will switch on. We will utilise tuple literals to switch the code on. And based on the literal evaluation we will express the result.

// we use tuple expression to switch on 
static string RockPaperScissors(string player1, string player2) =>
    (player1, player2) switch
    {
        // Tuple literals used to swich on 
        ("rock", "paper") => "Paper wins",
        ("rock", "scissors") => "Rock wins",
        ("paper", "rock") => "Paper wins",
        ("paper", "scissors") => "Scissors wins",
        ("scissors", "paper") => "Scissors wins",
        ("scissors", "rock") => "Rock wins",
        (_, _) => "it's a tie"
    };
Enter fullscreen mode Exit fullscreen mode

Tuple is a really interesting way to return or pass multiple values. C# syntax is optimise to take full benefit of tuples

Init Only Setters

It is a new feature added to C# 9 but its one of the features that will allow us to add immutability to our code. So let say we have a class or a type and we dont want to change the properties of the class to change after it has been initialised so this is a feature that will help us do that. So lets say we have this class

// this class has properties 
// every property has a getter and a setter, which will allow me to set and get
// the value 
class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Let us initiate a new employee from this class

var emp = new Employee
{
    FirstName = "Mohamad",
    LastName = "Lawand",
    Email = "mohamad@email.com"
};
Enter fullscreen mode Exit fullscreen mode

Because every property in the employee class has a setter i can change the value of the obj after it has been initialised

emp.FirstName = "Moe";
Enter fullscreen mode Exit fullscreen mode

If i want this type to be immutable i don't want to have outside calls to the obj to update its values in previews version of C# we could do something like this

// make the setter a private property which will remove access to the setter
class Employee
{
        public Employee(string fname, string lname, string email)
        {
            FirstName = fname;
            LastName = lname;
            Email = email;
        }

    public string FirstName { get; private set; }
    public string LastName { get; private set; }
    public string Email { get; private set; }
}

// or i can just remove the setter all together
class EmployeeOld
{
        public EmployeeOld(string fname, string lname, string email)
        {
            FirstName = fname;
            LastName = lname;
            Email = email;
        }

    public string FirstName { get; }
    public string LastName { get; }
    public string Email { get; }
}
Enter fullscreen mode Exit fullscreen mode

So if i want to initialise a new obj with the restriction of not able to update the properties after initialisation i would need to utilise constructor initialisation

var emp2 = new EmployeeOld
(
    "Mohamad", "Lawand", "mohamad@email.com"
);

emp2.FirstName = "Moe"; // this will throw an error
Enter fullscreen mode Exit fullscreen mode

but as you can see to implement this simple functionality we had to write more code and define more logic to implement it. In C# 9 we can achieve this very easily by updating our class to the following

class Employee
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
    public string Email { get; init; }
}
Enter fullscreen mode Exit fullscreen mode

This will allow me to initiate the obj but once its its initialise it will protect it from being mutated.

Default Interface Methods

When we think of interfaces in C# we look at them as the blue prints of our code. they let us know the methods and the properties we can make use when we are working with different classes or struct method

But now we have Default Interface Methods which will allow us to add like a default implementation to new method we want to add to exciting interfaces.

So lets say we have the following

interface IDoWork
{
    void workHard();
}

class Machine
{

}
Enter fullscreen mode Exit fullscreen mode

Now lets say i want to implement the interface in my class i need to do the following

class Machine : IDoWork
{
    public void workHard()
    {
        Console.WriteLine("I am doing work");
    }
}
Enter fullscreen mode Exit fullscreen mode

And we can initialise the Machine class and utilise the method like this

Machine machine = new ();
machine.workHard();
Enter fullscreen mode Exit fullscreen mode

So what happens if i want to add additional functionality to my code, i want to add new features or add new methods to my interface i can do something like the following

interface IDoWork
{
    void workHard();
    void takeABreak();
}
Enter fullscreen mode Exit fullscreen mode

But now all the classes the implements this interface will have an error since this method has not been implemented. Now we need to figure out a way to make our code to work and we have a couple of options we can explore to accomplish this.

i can create a new interface for the new functionality but now i have multiple interfaces which basically falls in the same category

The solution for this is to have a default implementation for the interface so in case this method has not been implemented by the class, there wont be any issue something like the following

interface IDoWork
{
    void workHard();
    void takeABreak(){
        // default empty implementation
    }
}
Enter fullscreen mode Exit fullscreen mode

This will allow me to make use of the existing interface, add functionality to it, remove the need to create a new interface which can make my code more messy as well make the the code structure better and my code will not be broken since i have not implemented the new method of interface in all of the classes which utilise the interface

i can make the default implementation as follow

interface IDoWork
{
    void workHard();
    void takeABreak(){
        // default empty implementation
        Console.WriteLine("This is not implemented");
        throw new NotImplementedException();
    }
}
Enter fullscreen mode Exit fullscreen mode

So in this case if a class tries to call this method without properly implementing it they will get notified.

This functionality will allow us to keep the maintability of the existing code so we wont break any of the existing code that is already there and in use but at the same time we are getting the benefit of adding new functionality for new features ..

Discussion (0)