We’ve been quite busy, my friends. In this C# 9 deep dive series, we’ve looked at init-only features, records, pattern matching, and then top-level programs. To complete this series (before showing off everything in a single app), we’ll discuss the last two items featured in the Build 2020 announcement: target typing and covariant returns. These are not related, but I’ve decided to bundle these in a single blog post.

This is the fifth post in a six-post series on C# 9 features in-depth:

Heads up! C# 9 is still in preview mode, so much of this content might change. I will do my best to update it as I come across it, but that is not guaranteed. Have fun, but your experience may vary.

This post covers the following topics.

Improved target typing

C# 9 includes improved support for target typing. What is target typing, you say? It’s what C# uses, normally in expressions, for getting a type from its context. A common example would be the use of the var keyword. The type can be inferred from its context, without you needing to explicitly declare it.

The improved target typing in C# 9 comes in two flavors: new expressions and target-typing ?? and ?:.

Target-typed new expressions

With target-typed new expressions, you can leave out the type you instantiate. At first glance, this appears to only work with direct instantiation and not coupled with var or constructs like ternary statements.

Let’s take a condensed Person class from previous posts:

public class Person
{
    private string _firstName;
    private string _lastName;

    public Person(string firstName, string lastName)
    {
        _firstName = firstName;
        _lastName = lastName;
    }
}

To instantiate a new Person, you can omit the type on the right-hand side of the equality statement.

class Program
{
    static void Main(string[] args)
    {
        Person person = new ("Tony", "Stark");
    }
}

A big advantage to target-typed new expressions is when you are initializing new collections. If I wanted to create a list of multiple Person objects, I wouldn’t need to worry about including the type every time I create a new object.

With the same Person class in place, you can change the Main function to do this:

class Program
{
    static void Main(string[] args)
    {
        var personList = new List<Person>
        {
            new ("Tony", "Stark"),
            new ("Howard", "Stark"),
            new ("Clint", "Barton"),
            new ("Captain", "America")
            // ...
        };
    }
}

Target typing with conditional operators

Speaking of ternary statements, we can now infer types by using the conditional operators. This works well with ??, the null-coalescing operator. The ?? operator returns the value of what’s on the left if it is not null. Otherwise, the right-hand side is evaluated and returned.

So, imagine we have some objects that shared the same base class, like this:

public class Person
{
    private string _firstName;
    private string _lastName;

    public Person(string firstName, string lastName)
    {
        _firstName = firstName;
        _lastName = lastName;
    }
}

public class Student : Person
{
    private string _favoriteSubject;

    public Student(string firstName, string lastName, string favoriteSubject) : base(firstName, lastName)
    {
        _favoriteSubject = favoriteSubject;
    }
}

public class Superhero : Person
{
    private string _maxSpeed;

    public Superhero(string firstName, string lastName, string maxSpeed) : base(firstName, lastName)
    {
        _maxSpeed = maxSpeed;
    }
}

While the code below does not get past the compiler in C# 8, it will in C# 9 because there’s a target (base) type that is convert-able:

static void Main(string[] args)
{
    Student student = new Student ("Dave", "Brock", "Programming");
    Superhero hero = new Superhero ("Tony", "Stark", "10000");

    Person anotherPerson = student ?? hero;
}

NOTE! As of today (07/14/20), this actually throws a compiler error. Based on the Microsoft post and other posts across the .NET community, this is the expected use case so this might be a temporary issue. Ah, the joys of playing with things in preview! While you may not be able to run this successfully today, the hope is that this is the end state. If not, I will update this section once I learn more.

Covariant returns

It has been a long time, coming—almost two decades of begging and pleading, actually. With C# 9, it looks like return type covariance is finally coming to the language. You can now say bye-bye to implementing some interface workarounds. OK, so just saying return type covariance makes me sound super smart, but what is it?

With return type covariance, you can override a base class method (that has a less-specific type) with one that returns a more specific type.

Before C# 9, you would have to return the same type in a situation like this:

public virtual Person GetPerson()
{
    // this is the parent (or base) class
    return new Person();
}

public override Person GetPerson()
{
    // you can return the child class, but still return a Person
    return new Student();
}

Now, you can return the more specific type in C# 9.

public virtual Person GetPerson()
{
    // this is the parent (or base) class
    return new Person();
}

public override Student GetPerson()
{
    // better!
    return new Student();
}

Wrapping up

In this post, we discussed how C# 9 makes improvements with target types and covariant returns. We discussed target-typing new expressions and their benefits (especially when initializing collections). We also discussed target typing with conditional operators. Finally, we discussed the long-awaited return type covariance feature in C# 9.

As this is still very much in preview, let me know any thoughts or if you’ve come across any issues!

Tags:

Updated:



Level up with The .NET Stacks Newsletter

If you enjoy my content, consider subscribing to The .NET Stacks, my weekly newsletter. It isn't a link blast! I go in-depth on news and trends, interview leaders in the community, and allow you to catch up with one resource.

    I don't do spam and will never share your address. Unsubscribe at any time.

    Leave a comment