Learn two of the most important features in C# 9.0 to make your code more concise, readable, flexible, and efficient. Credit: Thinkstock C# 9 introduced a number of features that allow us to write more efficient and flexible code. In previous articles we examined record types, static anonymous functions, relational and logical patterns, and top-level programs. In this article we’ll look at two more useful features in C# 9 — new target typing capabilities and covariant returns. Target typing refers to using an expression that gets its type from the context in which it is used, rather than specifying the type explicitly. With C# 9, target typing now can be used in new expressions and with conditional operators. Support for covariant return types in C# 9 allows the override of a method to declare a “more derived” (i.e., more specific) return type than the method it overrides. To work with the code examples provided in this article, you should have Visual Studio 2019 installed in your system. If you don’t already have a copy, you can download Visual Studio 2019 here. C# 9.0 is available in Visual Studio 2019 16.9 Preview 1 or later, or in the .NET 5.0 SDK. Create a console application project in Visual Studio First off, let’s create a .NET Core console application project in Visual Studio. Assuming Visual Studio 2019 is installed in your system, follow the steps outlined below to create a new .NET Core console application project in Visual Studio. Launch the Visual Studio IDE. Click on “Create new project.” In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed. Click Next. In the “Configure your new project” window, specify the name and location for the new project. Click Create. We’ll use this .NET Core console application project to work with target typing and covariant returns in the subsequent sections of this article. Use target typing with new expressions in C# 9 With C# 9 you now have improved support for target typing. There are two new ways in which you can implement target typing — in new expressions and with conditional operators. When using target-typed new expressions, you need not mention the type you want to instantiate. Let’s understand this with an example. Consider the following class: public class Author { private int Id { get; set; } private string FirstName { get; set; } private string LastName { get; set; } public Author(int id, string firstName, string lastName) { Id = id; FirstName = firstName; LastName = lastName; } } The following code snippet illustrates how you can use target typing to omit specifying the type when creating an instance of the class shown above. static void Main(string[] args) { var authors = new List<Author> { new (1, "Joydip", "Kanjilal"), new (2, "Dean", "Jones"), new (3, "Steve", "Smith") }; Console.Read(); } Use target typing with conditional operators in C# 9 With C# 9 it is now possible to infer types using conditional operators. Consider the Person class given below: public class Person { private int Id { get; set; } private string FirstName { get; set; } private string LastName { get; set; } public Person(int id, string firstName, string lastName) { Id = id; FirstName = firstName; LastName = lastName; } } Assume that there are two types of workers — an employee and a consultant. Here are the two classes that represent an employee and a consultant: public class Employee : Person { private string Department { get; set; } public Employee(int id, string firstName, string lastName, string department) : base(id, firstName, lastName) { Department = department; } } public class Consultant : Person { private int RatePerHour { get; set; } public Consultant(int id, int ratePerHour, string firstName, string lastName) : base(id, firstName, lastName) { RatePerHour = ratePerHour; } } The following code will compile in C# 9, but will not compile in earlier versions of C#: Employee employee = new Employee(1, “Joydip”, “Kanjilal”, “Development”); Consultant consultant = new Consultant(1, 150, “Joydip”, “Kanjilal”); Person person = employee ?? consultant; //Compilation error prior to C# 9 C# 9 lets you use target-typed conditional operators in ternary statements. Use covariant return types in C# 9 Covariant return types is a feature that enables you to override a method of a base class with a method in the derived class to return a more specific type. Earlier versions of C# did not allow returning a different type (than its base version) in an overridden method of a derived class. This changes in C# 9. For example, suppose you have two classes A and B and that the latter extends the former. If you have a virtual or abstract method in class A, you can override it in class B. You can do this because the C# programming language provides support for run-time polymorphism or late binding. However, until C# 9, you could not change the return type of the overridden method in the derived class. Let’s understand this with an example. Consider the updated version of the Person class given below. public class Person { private int Id { get; set; } private string FirstName { get; set; } private string LastName { get; set; } public Person() { } public Person(int id, string firstName, string lastName) { Id = id; FirstName = firstName; LastName = lastName; } public virtual Person GetPerson() { return new Person(); } } Note we’ve added a virtual method and a default constructor. Here we are returning an instance of the Person class from the GetPerson method by calling the Person class’s default constructor. Now consider the Employee class given below: public class Employee : Person { private string Department { get; set; } public Employee() { } public Employee(int id, string firstName, string lastName, string department) : base(id, firstName, lastName) { Department = department; } public override Employee GetPerson() { return new Employee(); } } And we’ve added a default constructor here too. Here an instance of the Employee class is being returned by the GetPerson method that overrides the Employee class. Interestingly, the return type of the GetPerson method is Employee in the Employee class. Note that the above code would not compile in C# versions before C# 9 because you’re constrained to use the same signature for your overridden methods in a derived class. Covariance and contravariance are longtime C# features (added with C# 4.0) that provide a polymorphic extension to delegates, arrays, and generics. Covariance enables you to use a more derived type (more specific) than originally specified, while contravariance enables you to use a less derived type (less specific). You can learn more about covariance and contravariance from Microsoft’s online documentation. How to do more in C#: How to use top-level programs in C# 9 How to use pattern matching in C# How to work with read-only collections in C# How to work with static anonymous functions in C# 9 How to work with record types in C# How to use implicit and explicit operators in C# Singleton vs. static classes in C# How to log data to the Windows Event Log in C# How to use ArrayPool and MemoryPool in C# How to use the Buffer class in C# How to use HashSet in C# How to use named and optional parameters in C# How to benchmark C# code using BenchmarkDotNet How to use fluent interfaces and method chaining in C# How to unit test static methods in C# How to refactor God objects in C# How to use ValueTask in C# How to use immutability in C# How to use const, readonly, and static in C# How to use data annotations in C# How to work with GUIDs in C# 8 When to use an abstract class vs. interface in C# How to work with AutoMapper in C# Related content feature 14 great preprocessors for developers who love to code Sometimes it seems like the rules of programming are designed to make coding a chore. Here are 14 ways preprocessors can help make software development fun again. By Peter Wayner Nov 18, 2024 10 mins Development Tools Software Development feature Designing the APIs that accidentally power businesses Well-designed APIs, even those often-neglected internal APIs, make developers more productive and businesses more agile. By Jean Yang Nov 18, 2024 6 mins APIs Software Development news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks news Go language evolving for future hardware, AI workloads The Go team is working to adapt Go to large multicore systems, the latest hardware instructions, and the needs of developers of large-scale AI systems. By Paul Krill Nov 15, 2024 3 mins Google Go Generative AI Programming Languages Resources Videos