Implementing method override covariance on C# (Part 2)

So, C# doesn’t allow us to change the return type when overriding methods. Could we do it in IL?

For reference, here is a C# version of the initial example in my previous blog post, and the resulting IL class & method signatures generated by the C# compiler:

Attempt 1: Implicit method overrides in IL

To help investigate this problem, I’ve created a small IL program that demonstrates the virtual method dispatch we need to subvert. Here is the output of the example without override covariance:

To call a method in IL, you need to use the call instruction, which just calls the method directly, or callvirt, which calls the method with virtual dispatch. For example, to call System.Object.ToString() on an object currently on the stack, the instruction is:

Here we can immediately see a problem – the method token indicating which method to call includes the return type (this is why methods can be overridden by return type in IL). And, as we can see above, IL method overridding is done implicitly by signature, by missing out the newslot keyword in the method declaration.

It follows that if we change B.Method to return a string (example code), dynamic dispatch doesn’t work anymore, as it is now a completely different method to A.Method:

Attempt 2: Explicit method overrides in IL

OK, so implicit overriding won’t work. However, in IL there is an .override directive that can be applied to methods, which specifies explicit method overrides. Could we use this to force covariant method overrides by adding this to the B.Method declaration (example here)?

Unfortunately not – although it compiles ok, the resulting executable fails verification, and throws an exception when you try to run it:

So it seems we can’t do exactly what we want using IL.

Taking a step back

Might we be able to create something that has the same behaviour as covariant method overrides without actually overriding directly? A workaround currently exists in C# for interfaces where you can create methods that behave the same as covariant interface methods using explicit implementations:

If we could do something similar with class overrides, we would be able to simulate covariant method overrides in the same way. In my next post, I’ll be looking at ways of doing exactly that.