DEV Community

Dave Brock
Dave Brock

Posted on • Originally published at daveabrock.com on

C# 9 Deep Dive: Target Typing and Covariant Returns

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.

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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");
    }
}
Enter fullscreen mode Exit fullscreen mode

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")
            // ...
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

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 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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (0)