Srikanth Technologies

What's New In C# 9.0

The following are the new features introduced in C# 9.0.

C# 9.0 is supported from .NET Core 5.0.

Init Only Setters

Init-only property is a property whose value can be set only in object initialization or constructor.

It is achieved by providing init accessor instead of set accessor, which can be used to change property value anytime.

The following example shows how to declare and use init-only properties.

class Person
{
        public string Name { get; init; }
        public int Age { get; init; }
        public Person()
        {

        }
        public Person(string name, int age)
        {
            Name = name;  // Assign values in constructor 
            Age = age;
        }
}

The above class creates two properties that can be initialized only either in constructor or in object initialization. The following code shows how to use them:

var p1 = new Person { Name = "Steve", Age = 20 };  // Assign values in object initialization 
var p2 = new Person("Jack", 30);

// Following statement is a compilation error as we can't change value of Age property 
p1.Age = 25;  

Records

A record is reference type that provides built-in functionality to encapsulate data. It provides an object that supports the following:

The following example shows how to create a record and perform basic operations without having to implement them ourselves.

// Immutable properties Hours, Minutes, Seconds are created from positional parameters
public record Time(int Hours, int Minutes, int Seconds)
{       
    public int MilliSeconds { get; init; }   // Init-only property 
}

// Record with mutable properties 
public record Customer
{
    public string Name { get; set; }
    public string Email { get; set; }
};

Now it is possible to create object of Time by passing required parameters to constructor. It also provides ToString(), which formats all values of properties, value comparison.

var t1 = new Time(10, 20, 30) { MilliSeconds = 100 };
Console.WriteLine(t1);             // calls built-in ToString() 

var t2 = new Time(10, 20, 30);
Console.WriteLine(t1.Hours);       // Access immutable property 
 
Time t3 = t1 with { Hours = 20 };  // Make a copy of t1 and change only Hours to 20 
Console.WriteLine(t3);

var c1 = new Customer { Name = "A", Email = "a@gmail.com" };
var c2 = new Customer { Name = "A", Email = "a@gmail.com" };

Console.WriteLine(c1 == c2);       // Uses built-in value equality  

c2.Email = "a@yahoo.com";          // Can modify through setter method 
Console.WriteLine(c1 == c2);

The above program generates the following output:

Time { Hours = 10, Minutes = 20, Seconds = 30, MillSeconds = 100 }
10
Time { Hours = 20, Minutes = 20, Seconds = 30, MillSeconds = 100 }
True
False

Top-level programs

It is now possible to write code directly in .cs file without any class and Main(). Whatever code is written at the top-level is automatically encapsulated into Main() by C#.

Here is a program that uses top-level statements.

using System;
using CsharpDemo;   // Namespace for Person class 

Console.WriteLine("Hello!");
Console.WriteLine($"No. of command line arguments : {args.Length}");   // args is available 

// Use other classes of the project - CsharpDemo.Person
var v1 = new Person { Name = "Anders", Age = 55 };
Console.WriteLine(v1.Name);
But this can be done only in of the programs in the project. If a project has two files with top-level statements, it is an error.

Command line arguments can still be accessed using args variable.

You should not specify StartupObject in .csproj file as code to execute is automatically taken from the file (.cs) which contains top-level statements.

Pattern Matching Enhancements

C# 9.0 includes new pattern matching options. These new options include relational operators and logical operators.

 int a = 150;
 char ch = ':';

 Console.WriteLine(a is >= 10 and <= 20 or  >=100 and <= 200);   // True
 Console.WriteLine(ch is '.' or ',' or ':' or ';');  // True
 Console.WriteLine(ch is (>='A' and <='E') or (>= 'a' and <= 'e'));  // False

 string msg = ch switch
 {
   '.' => "Dot",
   ':' => "Colon",
   ';' => "Semicolon",
   '+' or '-'  => "Operator",
   _ => "Unknown"
 };
             
 Console.WriteLine(msg);   // Colon

New Expression

It is possible to create an object with just new keyword without class name when the type of object is known. It means we don't have to give class name after new keyword and instead directly provide parameters for constructor.

Here is an example demonstrating how we can create an object of Person class without mentioning Person whenever the target type (Person) is known to compiler.

void Print(Person p)
{
   // code
}


// Create an object of type Person 
Person p1 = new() { Name = "Abc", Age = 20 };   // Using object initializer 

// Passing parameter of type Person
Print(new("Abc", 20));