Click here to monitor SSC

Implementing method override covariance on C# (Part 1)

Published 14 July 2010 4:23 pm

It has been said by many people that C# is ‘Java done right’ – it has many features that Java has, but implemented in a more sensible fashion (for example, generics) or concepts that wrap common coding patterns (properties and events). However, there is one feature not included in C# that I do miss from my time in Java – method override covariance (note that this is separate to generic co/contra-variance, which was added in C# 4; there’s an FAQ about that, and an article on wikipedia about covariance in general). As part of my project for the second Down Tools Week, I decided to see if anything could be done to add this small but useful feature to C#.

When a class overrides a method from its parent, Java allows you to change the overriding method return type to a subtype of the overridden method return type. For example:

class A {
public Object method() { return new Object(); }
}

class B extends A {
@Override
public String method() { return new String(); }
}

This is entirely type safe, as an instance of a subtype can always be used wherever an instance of the parent type is required.

In the code above, when you call method on a reference of type A, you get a reference to an Object back. When called on a reference of type B, you get a String. Furthermore, B.Method overrides A.Method for virtual method dispatch (as enforced by the @Override annotation):

A a = new A();
B b = new B();
A ab = new B();

Object ao = a.method(); // ao is an Object
String bo = b.method(); // bo is a String
Object abo = ab.method(); // abo is a String

This is most useful when dealing with factory classes:

class Foo1 {}
class Foo2 extends Foo1 {}

class Foo1Factory {
public Foo1 getFoo() { /* ... */ }
}

class Foo2Factory extends Foo1Factory {
@Override
public Foo2 getFoo() { /* ... */ }
}

When you’ve got code that takes an instance of Foo2Factory, getFoo will be typechecked by the compiler as returning a Foo2, rather than a Foo1 – you don’t have to cast to Foo2 manually.

C#, however does not allow you to do this. Compiling the C# version of the first example above produces the following error:

error CS0508: 'B.Method()':
return type must be 'object' to match overridden member 'A.Method()'

The result of this is that whenever you need to use a subtype returned by an overridden method, you need to insert a cast to the subtype yourself:

A ab = new B();
string abo = (string)ab.Method();

Although a very small issue, I find this restriction very annoying when writing code dealing with lots of factory classes. It feels like something the compiler should be helping me with, but isn’t. In the next post, I’ll look at solving this problem in IL.

Leave a Reply