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:
- Top-level statements
- Init-only properties
- Records
- Target-typed new expressions
- Improved Pattern Matching
- Pattern Matching in Switch Expressions
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.
Leave a Reply