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
Create a console application
dotnet new console -n CSharpHighlights
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");
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");
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");
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")
};
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")
};
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");
}
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
};
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
};
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"
};
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; }
}
Let us initiate a new employee from this class
var emp = new Employee
{
FirstName = "Mohamad",
LastName = "Lawand",
Email = "mohamad@email.com"
};
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";
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; }
}
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
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; }
}
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
{
}
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");
}
}
And we can initialise the Machine class and utilise the method like this
Machine machine = new ();
machine.workHard();
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();
}
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
}
}
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();
}
}
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 ..
Top comments (0)