List of C# 9 features
Tags: csharp, dotnet
Published: 2021-06-29 23:48:29

---
title: List of C# 9 features
published: true
description: 
tags: csharp, dotnet
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ybpz1xe6qg5xtfu41r92.png
---


## Top-level statements

The very simple program on C# looks like the following

```c#
using System;
namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}
```

but with C# 9 we can make it simpler:

```c#
System.Console.WriteLine("Hello World!");
```

## Target-Typed Object Creation

Let's imagine we have a class:

```c#
public class Book
{
	public string Title { get; set; }
	public string Author { get; set; }

	public Book()
	{

	}

	public Book(string title, string author)
	{
		Title = title;
		Author = author;
	}
}
```

Usually we create objects like follow:

```c#
var book = new Book();
// or
Book book = new Book();
```

With C# we can do:

```c#
Book book2 = new();
Book book3 = new("1", "A1");
```



## Init-only Setters

Consider we have a class `Book`

```c#
public class Book
{
        public string Title { get; set; }
        public string Author { get; set; }
}
```

and we can set values during the initialization. At the same time we can change the values as far as we have a setter for every field as well.

```c#
var book = new Book { Author = "1", Title = "2" };
book.Title = "2";
```

Imagine a situation where we want only set values during the initialization and we want to restrict them after initialization. In `C# 9` `Init-only` feature comes into play:



```c#
public class Book
{
        public string Title { get; init; }
        public string Author { get; init; }
}

var book = new Book { Author = "1", Title = "2" };
book.Title = "2"; // compile error
```



## Relational & Logical Patterns

Relational patterns permit the programmer to express that an input value must satisfy a relational constraint when compared to a constant value

```c#
public class Book{	public string Title { get; set; }	public string Author { get; set; }	public static decimal Postage(decimal price) => price switch	{		< 20 => 6.99m,		>= 20 and < 40 => 5.99m,		>= 40 and < 60 => 2.99m,		_ => 0	};}
```



## Records

We have a class:

```c#
public class Book{    public string Title { get; }    public string Author { get; }	public Book(string title, string author)    {          Title = title;          Author = author;    }}
```

And let's imagine we want to be able to create a book. We can do it as following:

```c#
var book = new Book("Title1", "Author1");
```

and also we want to serialize the object and de-serialize

```c#
var json = JsonSerializer.Serialize(book);Console.WriteLine(json);var book2 = JsonSerializer.Deserialize<Book>(json);var isEqual = book == book2;Console.WriteLine($"book == book2: {isEqual}"); // false
```

In console we'll see that de-serialized book is not the same book that was serialized. How can we make them the same? We can override equals operator:

```c#
public static bool operator ==(Book left, Book right) =>	left is object ? left.Equals(right) : right is null;
```

if we override `==` operator we also have to override `!-` operator:

```c#
public static bool operator !=(Book left, Book right) => !(left == right);
```

But we have not done anything to compare our object yet, so we have to override `Equals` method:

```c#
public override bool Equals(object obj) => obj is Book b && Equals(b);public bool Equals(Book other) =>	other is object &&	Title == other.Title &&	Author == other.Author;
```

And we need to override `GetHashCode` and `ToString` methods.

```c#
public override int GetHashCode() => HashCode.Combine(Title, Author);public override string ToString() => $"{Title} - {Author}";
```

Also, we have to make our class implement `IEquatable<T>` interface.

```c#
public class Book : IEquatable<Book>
```

Finally we have to write a bunch of code for just one simple action. Full class:

```c#
public class Book : IEquatable<Book>{   public string Title { get; }   public string Author { get; }   public Book(string title, string author)   {       Title = title;       Author = author;   }   public static bool operator ==(Book left, Book right) =>       left is object ? left.Equals(right) : right is null;   public static bool operator !=(Book left, Book right) => !(left == right);   public override bool Equals(object obj) => obj is Book b && Equals(b);   public bool Equals(Book other) =>       other is object &&       Title == other.Title &&       Author == other.Author;   public override int GetHashCode() => HashCode.Combine(Title, Author);}
```



Now `Console.WriteLine($"book == book2: {isEqual}");` will output `true`.

There is a lot of boilerplate code.

Moreover, if we add a new field we will have to update every method.

With `C# 9` we can use the `record` type for the same behavior. It allows making behavior for classes as if they were structures.

```c#
record Book(string Title, string Author)
```



## Extended partial methods

Now we can use modifiers and return values for partial methods.

```c#
public partial class Book{	public string Title { get; set; }	public string Author { get; set; }	public decimal Price { get; set; }	private partial decimal SetPrice();}public partial class Book{	private partial decimal SetPrice()	{		return 0m;	}}
```



## Covariant returns

In `C# 9` we can return derived types in overridden methods.

```c#
public class Book{	public string Title { get; set; }	public string Author { get; set; }}public class CollectionBook : Book{	public string Edition { get; set; }}public abstract class BookService{	public abstract Book GetBook();}public class CollectionBookService : BookService{	public override CollectionBook GetBook()	{		return new CollectionBook();	}}
```



Code: https://github.com/platinum-team/tech-talk-csharp9-features

Cheers!