C# 9.0: Covariant Return Types – Specify More Specific Return Types in Overridden Methods and Properties

In the previous blog posts you learned about different C# 9.0 features:

In this blog post, let’s look at another very interesting feature of C# 9.0, the covariant return types.

Hey Thomas, What Is Covariance?

Good question. In C#, you can return since version 1.0 more specific types from properties and methods. Of course, object-oriented languages like C# let you do that. When a programming language let’s you express that more specific return type, this is called covariance. And exactly that covariance is added in C# 9.0 for the return types of overridden methods and properties.

Let’s look at an example.

Work Without Covariance

Look at the following class structure. The Employee class has a virtual GetSkills method that returns an EmployeeSkills object.

public class Employee
{
  public string? FirstName { get; set; }
  public virtual EmployeeSkills GetSkills()
  {
    return new EmployeeSkills 
    { 
      CanSendEmails = true 
    };
  }
}

public class EmployeeSkills
{
  public bool CanSendEmails { get; set; }
}

On an Employee instance, you can call its GetSkills method to get the skills of the employee, like you see it below. So far so good.

var employee = new Employee { FirstName = "Thomas" };
EmployeeSkills skills = employee.GetSkills();

Now let’s say that the developers in your company have developer-specific skills. Of course they have. :-) So, you create a DeveloperSkills class that inherits from EmployeeSkills. I’ve added here a bool property that indicates whether a dev knows .NET or not:

public class DeveloperSkills : EmployeeSkills
{
  public bool KnowsDotNet { get; set; }
}

For the developers in your company you also create a subclass of Employee that I call Developer. As you can see in the next code snippet, that Developer class overrides the GetSkills method of the Employee class. It returns a DeveloperSkills object.

public class Developer : Employee
{
  public override EmployeeSkills GetSkills()
  {
    return new DeveloperSkills
    {
      CanSendEmails = true,
      KnowsDotNet= true
    };
  }
}

But now, as you can see in the code snippet above, the return type of the overridden GetSkills method is still EmployeeSkills. This means, when you create and use a Developer, you have to cast the returned object of the GetSkills method to a DeveloperSkills object if you like to use its KnowsDotNet property. You see this in the following code snippet:

var developer = new Developer { FirstName = "Thomas" };
DeveloperSkills skills = (DeveloperSkills)developer.GetSkills();
Console.WriteLine($"Email: {skills.CanSendEmails}");
Console.WriteLine($".NET: {skills.KnowsDotNet}");

Now, wouldn’t it be great if C# would let you express that a Developer has a more specific skills object? Being able to express this with C# is what is known as covariance. C# 9.0 allows you to define more-specific return types in overridden methods and in overridden readonly properties.

Covariant Return Types in C# 9.0

With C# 9.0 you can override the Employee‘s GetSkills method like we did it in the previous section. But you can specify now the more specific DeveloperSkills class as a return type:

public class Developer : Employee
{
  public override DeveloperSkills GetSkills()
  {
    return new DeveloperSkills
    {
      CanSendEmails = true,
      KnowsDotNet = true
    };
  }
}

This means, when you use a Developer object in your code, and when you call its GetSkills method, you will actually get a DeveloperSkills object without the need of a casting:

var developer = new Developer { FirstName = "Thomas" };
DeveloperSkills skills = developer.GetSkills();
Console.WriteLine($"Email: {skills.CanSendEmails}");
Console.WriteLine($".NET: {skills.KnowsDotNet}");

That’s great, isn’t it?

In the next code snippet you can see the full class structure that I used in this blog post. Note how the Developer class overrides the Employee‘s GetSkills method and specifies the more specific return type.

public class EmployeeSkills
{
  public bool CanSendEmails { get; set; }
}

public class DeveloperSkills : EmployeeSkills
{
  public bool KnowsDotNet { get; set; }
}

public class Employee
{
  public string? FirstName { get; set; }
  public virtual EmployeeSkills GetSkills()
  {
    return new EmployeeSkills
    {
      CanSendEmails = true
    };
  }
}

public class Developer : Employee
{
  public override DeveloperSkills GetSkills()
  {
    return new DeveloperSkills
    {
      CanSendEmails = true,
      KnowsDotNet = true
    };
  }
}

Summary

You learned in this blog post that C# 9.0’s covariant return types allow you to specify more specific return types in overridden methods and in overridden readonly properties. It’s a simple, but powerful feature.

Share this post

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.