Software Development Blogs

Software Development

C# 9.0 -> What’s new

.NET 5, C# 9.0 (and many more features) is released now. Like always, its the time we go through the new handy features that came with the latest version. In this blog, the focus will be on C# 9.0 only. I will write another one with .NET 5 features.

Immutable properties with ” init ” Keywords

I loved this one, as I am personally not a big fan of instance constructors. Though, mostly I have to write them because of the readonly (or get only) properties. Every time, we had to create a new readonly property in the class, the constructor needed to be modified (or a new constructor is required). I hardly enjoyed that process.

With C# 9.0 we have init properties. This property is the same as readonly properties but can be set outside of the constructor once during the initialization. Below is an example :

public class Person
{

// Surname can only set during initialization
public string SurName { get; init; }
}

// Setting the value of Surname
var gPerson = new Person() { SurName = “S”};

Init accessors with readonly fields

This feature is an extension of init properties. Before C# 9.0, compiler allowed to set a readonly field in the constructor only. In the latest update, we can set from init as well. Here’s an example

public class Person
{
private readonly string firstName = “”;
public string Name { get => firstName; init => firstName = value; }
public string SurName { get; init; }
}

var gPerson = new Person() { Name = “G”, SurName = “S”};

Records

Record is one of the more famous features of C# 9.0. Everyone is talking about it. It does add a lot of value.

The use case for this would be when we want the whole object to work like a value-type & stay immutable. An example comes to my mind is recording sharing between threads or processes. Below is how we declare a record:

public record Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
}

Records are like classes, but their identity is represented by their content, not by their reference. Very close to a struct, but still a reference type.

With-Expression

One of my favourite feature that comes with the record. Cloning of an object to a new one has always been available, but it has never been this clean & readable. “with” is only available for records for now. Below is an example

public record Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
}
var gPerson = new Person() { FirstName = “G”, LastName= “S”};

// cloning the object with a change in FirstName
var mPerson = gPerson with {FirstName = “M”};

Value-Based equality

There are many times when we want to compare our object instances based on values. Till now we achieved it either by implementing IComparer, IComparable or by override Equals method (or some other methods). C# 9, Records allow us to achieve value-based equality with all the operators as we do with any value type.

public record Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
}
var gPerson = new Person() { FirstName = “G”, LastName= “S”};
var mPerson = new Person() {FirstName = “G”, LastName = “S”};

// this results in “True”
Console.WriteLine($”{gPerson==mPerson}”);

Important to note that, ReferenceEquals would still return False for the records with the same content. So If you need to compare the objects for references its still possible.

This code for above objects would return false.
Console.WriteLine($”{ReferenceEquals(gPerson,mPerson)}”);

Top-Level Statements

Top-level statements remove unnecessary ceremonies from the program start. Below is standard practice to start a c# program.

using System;
namespace net5pointzero
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(“Hello World”);
Console.ReadLine();
}
}
}

This now can be replaced by simply

using System;
Console.WriteLine(“Hello World”);
Console.ReadLine();

Most importantly, top-level statements don’t limit your application’s scope or complexity. Those statements can access or use any .NET class. They also don’t limit your use of command-line arguments or return values. Top-level statements can access an array of strings named args. If the top-level statements return an integer value, that value becomes the integer return code from a synthesized Main method. The top-level statements may contain async expressions. In that case, the synthesized entry point returns a Task or Task.

Pattern-Matching enhancements

This improves the readability of data comparison by miles. This topic would need more attention than a small paragraph, so I would recommend going to this link and exploring this feature more.

Tutorial: Build algorithms with pattern matching | Microsoft Docs

Target-typed “new” expressions

“Target typing” is a term we use for when an expression gets its type from the context of where it’s being used. For instance, null and lambda expressions are always target type.

new expressions in C# have always required a type to be specified (except for implicitly typed array expressions). In C# 9.0 you can leave out the type if there’s a clear type that the expression is being assigned to.

Point p = new (3, 5);
Point[] ps = { new (1, 2), new (5, 2), new (5, -3), new (1, -3) };

And much more…

Please follow the link below for more information

What’s new in C# 9.0 – C# Guide | Microsoft Docs

Leave a Reply