Four features in C# 9 that aren't in C# 8

Published: Tuesday 29 September 2020

We will try out four new features in C# 9 that were not in C# 8.

At time of writing, C# 9 is still in preview and is due to be released alongside the full release of .NET 5, before the end of 2020.

So, if you are interested in trying them out yourself and you are using Visual Studio, you need to make sure you have Visual Studio 2019 16.8 or higher.

At present, VS 2019 16.8 is in preview mode, so you will need to download the VS 2019 preview version if you are looking to try these new features.

Some of the C# 9 features work in VS 2019 16.7, but not all of them. And there are still some features that have not been complete.

However, there are plenty of features that have been complete. So, we are going to have a look at four of the features that we can try out for ourselves.

Feature #1: Target-Typed Object Creation

There is a new way where we can create a new instance of a particular type. Rather than explicitly calling the type after the 'new' expression, we can now create a variable with that type, and simply use the 'new' expression.

Take the example below. In C# 8, if we wish to create a new instance of Product, we would need to explicitly declare which type we were creating a new instance of.

class Program
{
	static void Main(string[] args)
	{
		Product myProduct = new Product();
	}       
}
 
public class Product
{
	public string Name { get; set; }

	public string Sku { get; set; }
}

However, this can be simplified in C# 9 by just calling the new keyword, wrapped with brackets:

Product myProduct = new();

And it works if we have multiple constructors. So if we decide we want to pass in the Name and Sku into the constructor, we can do that as well.

class Program
{
	static void Main(string[] args)
	{
		Product myProduct = new("My Product", "PRODUCT-001");
	}       
}
 
public class Product
{
	public Product()
	{

	}

	public Product(string name, string sku)
	{
		Name = name;
		Sku = sku;
	}

	public string Name { get; set; }

	public string Sku { get; set; }
}

Feature #2: Init-Only Setters in Properties

Whilst we are on the subject of initialisation, we can now set a property setter as "init". What that means is that we can only set the property when an instance of the type is created.

public class Product
{
	...
	public string Name { get; init; }
	...
}

In the past, if we wanted to set a property outside the class, we would have to make the property publicly available to be able to be set.

And, if we only wanted to set a property on initialisation, we would have to pass it as a parameter in the constructor, and set it inside the constructor.

But now with properties, we can use the "init" keyword. What this means is that we can create a new instance of a type, we can set the property without passing any parameters in the constructor.

However, if we try and set the property after the initialisation, it will throw a compile error.

class Program
{
	static void Main(string[] args)
	{
		Product myProduct = new Product { Name = "My Product", Sku = "SKU-001 " };

		myProduct.Name = "My Product 2"; // This would throw an error
	}       
}
 
public class Product
{
	public Product()
	{

	}

	public string Name { get; init; }

	public string Sku { get; init; }
}

Feature #3: Covariant Returns

Covariant returns is where we can override a method and return a type that inherits the original type.

public class Product
{
	public Product()
	{

	}

	public string Name { get; init; }

	public string Sku { get; init; }
}
 
public class CameraProduct : Product
{
	public string TypeOfCamera { get; set; }
}
 
public abstract class ProductService
{
	public abstract Product GetProduct();
}
 
public class CameraProductService : ProductService
{
	public override CameraProduct GetProduct()
	{
		return new CameraProduct();
	}
}

Take the example above. We have a CameraProduct class that inherits the Product class.

Then, we have a CameraProductService class that inherits the ProductService class. Inside the ProductService class, we have an abstract method of GetProduct, that returns an instance of Product.

In the CameraProductService class, we are overriding the GetProduct method. However, instead of returning an instance of Product, we can return an instance of CameraProduct.

That's because CameraProduct is a child of Product.

In past versions of C#, we would have had to of returned an instance of Product.

Feature #4: Relational & Logical Patterns

Switch patterns have made a significant change in C# 9.

Now, we can have relational patterns so we can set conditions such as "greater than" or "less than".

In addition, we can have logical patterns. This means we can use keywords like "and" or "or" to set a condition when declaring a switch statement.

static double Postage(double price) => price switch
{
	< 20 => 5.99,
	>= 20 and < 40 => 3.99,
	>= 40 and < 60 => 2.99,
	_ => 0
};

Bonus Feature: Extending Partial Methods

We are going to look at one more feature. This is where we can extend partial methods.

In previous versions of C#, partial methods were limited.

We were unable to set an access modifier, like "private" or "public". They also had to return a void.

However in C# 9, partials have been extended so we can now set an access modifier and also return a type.

This is demonstrated in the video below:

In addition, the video will demonstrate and cover the four other C# methods we have covered in this article.

Using Visual Studio, we write the code and explain the differences from previous C# versions.

More C# Features To Come

There are more features for C# 9 to come.

There is a list of some of the changes on the Microsoft blog.

In addition, by viewing the "Language Feature Status" document, there is a full list of all the features in C# 9, the progress of each feature and documentation as to how each feature is meant to work.