﻿# What's new in C\# 9: overview

Although C\# 9 came out over half a year ago, the community is still processing its immense list of changes and has yet to discover best practices for the new features\. It's a great excuse to go over C\# 9 features one more time\.

![0860_OverviewCSharp9/image1.png](https://import.viva64.com/docx/blog/0860_OverviewCSharp9/image1.png)

## Properties for initialization only

C\# 9 got a new keyword \- _init_\. After an object is initialized, its properties that have the _init_ keyword cannot be changed\. Was something like this possible before? Yes, you could employ a constructor and do something similar \- but using an initializer to do this wouldn't have worked\. 

```cpp
public class PersonClass
{
    public string Name { get;}
    public string Surname { get; set; }
}

public static void Main()
{
    var person = new PersonClass() { Name = "Silver", Surname = "Chariot" };
    //Error CS0200
    //Property or indexer 'PersonClass.Name' cannot be assigned
    //to --it is read only
 }
```

Let's change the code and use _init_:

```cpp
public class PersonClass
{
    public string Name { get; init; }
    public string Surname { get; init; }
}

public static void Main()
{
    var person = new PersonClass() { Name = "Silver", Surname = "Chariot" };
    //No error
    person.Name = "Hermit";
    //Error CS8852
    //Init-only property or indexer 'PersonClass.Name' can only be assigned
    //in an object initializer, or on 'this' or 'base'
    //in an instance constructor or an 'init' accessor.
}
```

## Records

### What are they?

One of the main new features in C\# 9 is a new type \- _record_\. A record is a new **reference** type that you can create instead of classes or structures\. To see how it's different from the other two, let's see what the new type can do\.

### Positional syntax

We can see new features from the very beginning\. Of course, you can employ the old approach and define a record similarly to classes and structures\. There is, however, a short way to do this: 

```cpp
public record PersonRecord(string Name, string Surname);
```

The construct expands as follows:

```cpp
public record PersonRecord
{
    public string Name { get; init; }
    public string Surname { get; init; }

    public PersonRecord(string name, string surname)
    {
        Name = name;
        Surname = surname;
    }
    public void Deconstruct(out string name, out string surname)
    {
        name = Name;
        surname = Surname;
    }
}
```

Okay, the deconstructor is new, what else? Correct, instead of _set_, there's the _init_ keyword I've mentioned earlier\. Consequently, by default, records are immutable \- and that's exactly the cases for which they are intended\.

When you initialize variables, the deconstructor allows you to get the values of all parameters of the declared record:

```cpp
var person = new PersonRecord("Silver", "Chariot");
var (name, surname) = person;
```

You won't be able to change this record:

```cpp
person.Name = "Hermit";
//Error CS8852
//Init - only property or indexer 'PersonRecord.Name' can only be assigned
//in an object initializer, or on 'this' or 'base'
//in an instance constructor or an 'init'
```

In the previous examples, I did not define a body for the shortened form \- but this can be done\. Moreover, you can change property values, if you don't like the ones that have been created automatically:

```cpp
public record PersonRecord(string Name, string Surname)
{
    public string Name { get; set; } = Name;
    public string Surname { get; set; } = Surname;
    public string GetFullName()
        => Name + " " + Surname;
}
public static void Main()
{
    var person = new PersonRecord("Hermit", "Purple");
    person.Name = "Silver";
    Console.WriteLine(person.GetFullName());
    //Silver Purple
}
```

### Value equality

As we know, structures do not have overridden comparison operators\. When comparing class instances, we do not compare data inside objects, but references to them\. Now let's take a look at how this happens for records:

```cpp
public record Person(string Name, string Surname);

public static void Main()
{
    var first = new Person("Hermit", "Purple");
    var second = new Person("Hermit", "Purple");
    Console.WriteLine(first == second);
    //true
}
```

Yes, that's right \- the comparison is based on record **field** values\. The "\=\=" and "\!\=" operators and the _Object\.Equals\(Object\)_ method are overridden, so we do not need to worry about them\.

### The ToString method

Talking about overridden methods\. _ToString_ is also overridden\. While for structures and classes this method returns their names, for records it also returns the contents:

```cpp
var personRecord = new PersonRecord("Moody", "Blues");
var personStruct = new PersonStruct("Moody", "Blues");
var personClass = new PersonClass("Moody", "Blues");

Console.WriteLine(personRecord.ToString());
Console.WriteLine(personStruct.ToString());
Console.WriteLine(personClass.ToString());

//PersonRecord { Name = Moody, Surname = Blues }
//PersonStruct
//PersonClass
```

### Inheritance

I haven't had a chance to mention that in IL code records are classes\. Although this is true, it would be incorrect to say that they are the same\. While records do support inheritance, you cannot inherit records from classes\. However, records can implement interfaces\.

There are a few interesting points about inheritance as related to records\. Take a look at this example:

```cpp
public record Person(string Name, string Surname);
public record PersonEnglish(string Name, string MiddleName, string Surname)
    : Person(Name, Surname);

public static void Main()
{
    var person = new Person("Tom", "Twain");
    var englishPerson = new PersonEnglish("Tom", "Finn", "Twain");

    Console.WriteLine(englishPerson);
    //PersonEnglish { Name = Tom, Surname = Twain, MiddleName = Finn }

    var (one, two, three) = englishPerson;
    Console.WriteLine(one + " " + two + " " + three);
    //Tom Finn Twain
}
```

Child records have the same overridden methods as their parents\. However, unexpectedly, the order of property values, that the _ToString_ method and the deconstructor return, differs\. Do keep this in mind\.

You can see another interesting thing when comparing records\. Inspect the following code:

```cpp
public record Person(string Name, string Surname);
public record Teacher(string Name, string Surname, int Grade)
    : Person(Name, Surname);
public record Student(string Name, string Surname, int Grade)
    : Person(Name, Surname);
public static void Main()
{
    Person teacher = new Teacher("Tom", "Twain", 3);
    Person student = new Student("Tom", "Twain", 3);
    Console.WriteLine(teacher == student);
    //false
    Student student2 = new Student("Tom", "Twain", 3);
    Console.WriteLine(student2 == student);
    ///true
}
```

In this example, all instances have the same set of properties and property values\. Nonetheless, variables declared as _Person_ produce _false_ when compared, while comparing _Person_ to _Student_ yields _true_\. This happens, because the comparison method takes the runtime type into account during comparison\.

### Reversible changes

You can use the _with keyword to create record instances based on existing record instances\._ This feature allows you to change indicated property values by way of object initialization syntax:

```cpp
var person = new Person("Tom", "Twain");
var another = person with { Name = "Finn" };

Console.WriteLine(another);
//Person { Name = Finn, Surname = Twain } 

var another2 = another with { };
Console.WriteLine(another == another2);
//true
```

In order for a property to be able to use the _with_ keyword, this property needs to have the _set_ or _init_ access method, because, as we've already discussed, the initializer does not work without them\.

### Use cases

Why did developers of C\# add the _record_ type? As I've already said, records are **assumed** immutable\. Consequently, they are best suited in scenarios that require an immutable data set \(without methods\)\. This includes functional programming where programs are sequences of pure functions and where immutability is very important\.

Another obvious use case is the [DTO](https://en.wikipedia.org/wiki/Data_transfer_object) pattern used in data exchange between an application's subsystems\. This applies mostly to web programmers that need to pass data between an application's layers, for example, registration models, login, and others\.

## Top\-level instructions

Good news: coding in C\# is now even more enjoyable\! At least for newbies and those who want to check something quickly\. For example, to write an article about the new C\#\.

Thanks to top\-level instructions, we no longer need to be tied to long namespace and class constructs and drag them around\. This means, "Hello World" many not look like this anymore:

```cpp
using System;

namespace TestApp
{
    class Program 
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}
```

But rather like this:

```cpp
using System;
Console.WriteLine("Hello World!");
```

Of course, you cannot write a real\-world multifunctional application like this \- only one file can have top\-level instructions\. But that was not the goal anyway\. Another thing to keep in mind is, our _Main_ method did not go anywhere\. Moreover, this is where our code is executed\. Because of this we cannot override the method \- and can get access to _args_:

```cpp
using System;
Console.WriteLine(args[0]);

static void Main(string[] args)
    //Warning CS7022: The entry point of the program is global code;
    //ignoring 'Main(string[])'{
    Console.WriteLine(args[1]);
}
```

Let's run the program in the console:

```cpp
TestApp.exe Hello World!
Hello
```

## Target typing

### new\(\)

Target typing refers to getting a variable's type from context\. That's exactly what developers of C\# 9 decided to improve\.

The first thing we see is the new\.\.\. _new_\. Basically, the new syntax of _new_ is _var_ in reverse\. If the variable's type is already known \(for example, from the declaration\), you can skip _new_ when writing expressions:

```cpp
ObservableCollection<string> collection = new();
Person person = new("Hermit", "Purple");
```

Unfortunately, C\# cannot read thoughts ~~so far~~, so it won't understand this expression:

```cpp
var collection = new();
//Error CS8754 There is no target type for 'new()'
```

So a fair question remails \- where do we use it? We already have the generally accepted _var_, and now we have two identical forms of short notation:

```cpp
var collection = new ObservableCollection<string>();
ObservableCollection<string> collection = new();
```

For a program's code this new feature may seem excessive\. However, there is one place where we have to indicate a type in declaration \- class members\. That's right, now you can decrease the amount of code inside a class's body\. You don't need these anymore:

```cpp
public Dictionary<int,List<string>> field = new Dictionary<int,List<string>>();
```

Here's what your code would look like in C\# 9:

```cpp
public class School
{
    ObservableCollection<Student> studentList = new();
    ObservableCollection<Teacher> teacherList = new();
}
```

### The ?? and ?: operators

The ternary operator can now understand arguments of different types better, which is why we can do without explicit casts:

```cpp
Person person = expr ? student : teacher;
```

In C\# 9 early preview stages, developers of C\# announced that the null\-coalescing operator will be able to process different types that have the same base class\. However, looks like this feature did not make it to the release:

```cpp
Person person = student ?? teacher;
//Error CS0019
//Operator '??' cannot be applied to operands of type 'Student' and 'Teacher'
```

## Covariant return type

In child classes, you can now override the return type\. Of course, there is no dynamic typing \- I am talking only about types linked by inheritance\. This change is intended to make life easier when the "[Factory method pattern](https://en.wikipedia.org/wiki/Factory_method_pattern)" is involved\. Here's an example: suppose, a video game has various types of merchants who sell goods of different types \(i\.e\. goods that have different characteristics\):

```cpp
public abstract class Item
{
    ....
}
public class MagicItem : Item
{
    ....
}
public class WeaponItem : Item
{
    ....
}
public abstract class Merchant
{
    ....
    public abstract Item BuyItem();
}

public class MagicianMerchant : Merchant
{
    ....
    public override MagicItem BuyItem() { return new MagicItem(); }
}

public class WeaponMerchant : Merchant
{
    ....
    public override WeaponItem BuyItem() { return new WeaponItem(); }
}

public static void Main()
{
    var magician = new MagicianMerchant();
    var blacksmith = new WeaponMerchant();

    MagicItem boughtItem1 = magician.BuyItem();
    WeaponItem boughtItem2 = blacksmith.BuyItem();

}
```

In the code above, you can see that in C\# 9 you can obtain the compile\-time types straight from the corresponding methods, and get immediate access to their own fields and methods\. Previously, it would have been necessary to do an explicit type cast:

```cpp
MagicItem boughtItem1 = (MagicItem)magician.BuyItem();
WeaponItem boughtItem2 = (WeaponItem)blacksmith.BuyItem();
```

By the way, if _Item_ were an interface, this feature would have worked\. And it would not have worked if _Merchant_ were an interface\.

## Static lambda expressions and anonymous functions

In C\#, when anonymous functions refer to local variables, the compiler allocates memory for a temporary object\. This is necessary, because an anonymous function can exist longer than the function that created it\. Now you can define lambda expressions and anonymous functions as static so that they do not overtake surrounding context, thus preventing memory allocation: 

```cpp
double RequiredScore = 4.5;
var students = new List<Student>() 
{ 
    new Student("Hermit", "Purple", average: 4.8),
    new Student("Hierophant", "Green", average: 4.1),
    new Student("Silver", "Chariot", average: 4.6)
};

var highScoreStudents =
    students.Where(static x => x.AverageScore > RequiredScore);
//Error CS8820
//A static anonymous function cannot contain a reference to 'RequiredScore'
```

In this scenario, passing references to constants is possible:

```cpp
const double RequiredScore = 4.5;
var students = new List<Student>() 
{ 
    new Student("Hermit", "Purple", average: 4.8),
    new Student("Hierophant", "Green", average: 4.1),
    new Student("Silver", "Chariot", average: 4.6)
};

var highScoreStudents =
    students.Where(static x => x.AverageScore > RequiredScore);
//No error
```

## Discard for anonymous and lambda function parameters

Here I'll mention one more small enhancement\. If we do not need parameters in an expression, you can leave an underscore in their place\. For example, if we do not need _sender_ and _EventArgs_, you can avoid the compiler's warning:

```cpp
button1.Click += (_, _) => ShowNextWindow();
```

You can indicate type if you need to:

```cpp
button1.Click += (object _, EventArgs _) => ShowNextWindow();
```

## GetEnumerator extension support

Now _foreach_ can recognize _GetEnumerator_ as an extension method, which means you can iterate through what was previously impossible\. The developers' [motivation](https://github.com/dotnet/csharplang/discussions/600) to introduce this feature were use cases like iterating through a tuple:

```cpp
public static class TupleExtensions
{
    public static IEnumerator<T>
        GetEnumerator<T>(this ValueTuple<T, T, T, T> tuple)
    {
        yield return tuple.Item1;
        yield return tuple.Item2;
        yield return tuple.Item3;
        yield return tuple.Item4;
    }
}
foreach(var item in (1, 2, 3, 4))
{
   //1 2 3 4
}
```

Now you can even iterate through _Range_:

```cpp
public static IEnumerator<Index> GetEnumerator(this Range number)
{
    for (Index i = number.Start; i.Value < number.End.Value; i = i.Value + 1)
    {
        yield return i;
    }
}

public static void Main()
{
    foreach (var i in 1..5)
    {
        Console.WriteLine(i);
        //1 2 3 4
    }
}
```

Instead of the range, you could do this for other types, for example, _int_\. What is wrong with this? Microsoft documentation [explicitly states](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/extension-getenumerator) that the ranges are not intended for this\. The most common recommendation is to use the _GetEnumerator_ extension method only when the application design justifies this\. This makes sense \- many code reviewers may be surprised when they see an enumerated _Object_\.

## Pattern matching enhancements

In C\# 9 we got more keywords: _and_, _not_, _or_\. They are used in template syntax, and with them, it is now possible to use comparison operators \(_<_, _<\=_, _\>_,_ \>\=_\) and parentheses\. Here is a comprehensive example that demonstrates such syntax in a logical expression:

```cpp
public static bool IsPasses(Student student)
{
    return student is ({ AverageScore: >= 4.4, } or { Privilege: true }) 
                   and not {Department: "Central" };
}

static void Main()
{
    Student nullCandidate = null;
    var candidate = new Student(name: "Tom", surname: "Twain",
        department: "South", privilege: false, score: 4.6);

    Console.WriteLine(IsPasses(nullCandidate));
    //false

    Console.WriteLine(IsPasses(candidate));
    //true
}
```

Of course, all of this is syntactic sugar\. You can use more classical syntax to implement the same method:

```cpp
public static bool IsPasses2(Student student)
{
    return    student != null
           && (student.AverageScore >= 4.4 || student.Privilege == true) 
           &&  student.Department != "Central";
}
```

By the way, note that in the new version of code the check for _null_ is automatic\. When comparing methods, you can also notice that the new syntax is significantly more readable\.

What's more, the enhanced pattern matching in C\# 9 also affected _switch_ statements\. Let's create a similar method:

```cpp
public static bool IsPassesCommon(Student student)
    => student switch
    {
        { Privilege: true} => true,
        { AverageScore: >= 3.5 } and {AverageScore: <= 4.5 } => true,
        _ => false
    };
```

By the way, before C\# 9, _switch_ expressions required the variable name \(or an underscore instead of it\) after the class name, even if this name was not used anywhere\. Now you can omit it:

```cpp
public static bool IsStudies(Person person)
    => person switch
    {
        Student => true,
        Teacher => false,
        _ => false
    };
```

## Local function attributes

Here everything is pretty clear\. You can now apply attributes to local functions\. For example, the _Conditional_ attribute:

```cpp
static void Main()
{
    [Conditional("DEBUG")]
    static void PrintDebug()
    {
        Console.WriteLine("This is debug mode");
    }

    PrintDebug();
    Console.WriteLine("Hello World!");
    //Debug:
    //This is debug mode
    //Hello World!

    //Release:
    //Hello World!
    }
}
```

## New data types and performance

To improve support of low\-level libraries that require high performance, C\# 9 introduced a few new data types and extension features for unsafe code\. Most developers probably do not need this, but it's a good idea to familiarize yourself with this new addition\.

New data types: [_nint_, _nuint_](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nint-nuint) and [_half_](https://devblogs.microsoft.com/dotnet/introducing-the-half-type/)\. It is easy to guess that the first two are integers whose size depends on the operating system's digit capacity: 4 bytes in 32\-bit systems, and 8 bytes in 64\-bit systems\. _half_ is a 16\-bit real number that is mainly intended to store information when there is no requirement for high precision\. Yes, I meant **only** storage, I did not include arithmetic operations\.

Two more enhancements that work only in the unsafe mode are the _SkipLocalsInit_ attribute to suppress _localsinit_ flags, as well as pointers to functions\. From the [documentation](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code): 

```cpp
public static T UnsafeCombine<T>(delegate*<T, T, T> comb, T left, T right) => 
    comb(left, right);
....
static int localMultiply(int x, int y) => x * y;
int product = UnsafeCombine(&localMultiply, 3, 4);
```

## Code Generators

### What are they?

Another big new feature is code generators\. It is so big it won't fit into this article's scope \- fortunately, there are already enough articles dedicated to this topic on the Internet\. Shortly speaking \- they allow you to check programs and supplement them with new code files during compilation\. However, I'd like to focus on syntax changes that came with the generators\. 

### Partial methods

Partial methods were upgraded to work better with code generators\. Partial methods existed before, but now they can have a return value, _out_ parameters, and access modifiers\. This means, they are now not that different from usual methods:

```cpp
public partial class Person
{
    public string Name { get; set; }
    public string Surname { get; set; }
    public Person(string name, string surname)
    {
        Name = name;
        Surname = surname;
    }
    public partial bool Speak(string line, out string text)
}
public partial class Person
{
    public partial bool Speak(string line, out string text)
    {
        if (string.IsNullOrEmpty(line))
            return false;

        text = Name + ": " + line; 
        Console.WriteLine(text);
        return true;
    }
}
```

It seems that now in C\#, it is possible to separate header files and implementation, as well as do a forward declaration\. C\+\+ crept up from where you did not expect it\.

I'll point out that if a partial method got an access modifier, the project will not compile without the implementation\.

### ModuleInitializerAttribute

The last addition is the _ModuleInitializer_ attribute\. It was introduced because libraries \- including code generators \- need initialization logic\. The compiler calls methods that have this attribute before accessing a field or calling a method inside the module\. [Documentation](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.moduleinitializerattribute?view=net-5.0) describes the following requirements for initialization methods:

* the method must be static;
* the method must be a regular member method \(i\.e\. not an access method, constructor, local function etc\.\);
* the method must not have parameters; 
* the method must return _void_; 
* the method must not be generic or be a part of a generic class;
* the method must be available from the containing module \(possess the _internal_ or _public_ access modifier\)\.

Here's an example:

```cpp
public class Program
{
    static string StartMessage;

    [ModuleInitializer]
    public static void Init()
    {
        StartMessage = "Hello World!";
    }

    static void Main()
    {
        Console.WriteLine(StartMessage);
        //Hello World!
    }
}
```

I've also mentioned that an application can have several initialization methods:

```cpp
public class Program
{
    static string StartMessage;

    [ModuleInitializer]
    internal static void Init1()
    {
        StartMessage = "Hello World!";
    }

    [ModuleInitializer]
    internal static void Init2()
    {
        StartMessage = "foo bar";
    }

    static void Main()
    {
        Console.WriteLine(StartMessage);
        //foo bar
    }
}
```

We cannot affect the order in which initialization methods will be called \(at least, there's no feature for it, per se\), but the order is always **the same**\. From this example, you might assume that methods are called one after the other and the result is obvious, but this is not so\. When initialization methods are in different parts of an application \(or just in different classes\), the result may be unexpected\. This is why it's better to logically separate them in order to avoid a state similar to the one above\. Finally, let's take a look at a peculiar example that someone may have been thinking about:

```cpp
[ModuleInitializer]
public static void Main()
{
    Console.WriteLine("Hello World!");
    //Hello World!
    //Hello World!
}
```

Yes, the method was shamelessly called twice\. I think it's clear why you shouldn't apply the ModuleInitializer attribute to the _Main_ method\.

## Conclusion

The list of new features in C\# 9 turned out to be pretty impressive\. A significant part of the enhancements is nothing more than syntactic sugar that does not fundamentally change anything, but does make developers' lives easier\. As for the rest, we excited to see how C\# keeps gradually developing to meet modern needs\.

If you want to study C\# 9 new features in more detail, take a look at [Microsoft's documentation](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9) that will link you straight to technical articles whose links I used in this text\.