Click here to monitor SSC

Simon Cooper

Implementing method override covariance on C# (Part 2)

Published Friday, July 16, 2010 9:50 PM

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:

public class A {
public virtual object Method() { /* ... */ }
}

public class B : A {
public override object Method() { /* ... */ }
}


.class public auto ansi beforefieldinit A extends [mscorlib]System.Object {

.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed { /* ... */ }

.method public hidebysig newslot virtual
instance object Method() cil managed
{ /* ... */ }
}

.class public auto ansi beforefieldinit B extends A {

.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed { /* ... */ }

// implicitly overrides A.Method
.method public hidebysig virtual
instance object Method() cil managed
{ /* ... */ }
}

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:

Calling A.Method on A: System.Object
Calling B.Method on B: System.String
Calling A.Method on B: System.String

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:

callvirt instance string [mscorlib]System.Object::ToString()
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:

Calling A.Method on A: System.Object
Calling B.Method on B: System.String
Calling A.Method on B: System.Object

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)?

.method public virtual instance string Method() cil managed {
.override A::Method
/* ... */
ret
}

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

System.MissingMethodException: Method not found: 'System.String A.Method()'
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:

public interface IA {
object Method();
}

public class B : IA {
public string Method() {
/* ... */
}

object IA.Method() {
return Method();
}
}

// when using these classes:
IA a = new B();
B b = new B();

object ao = a.Method();
string bo = b.Method();

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.

Comments

No Comments
You need to sign in to comment on this blog

About Simon Cooper

Simon joined Red Gate as a software developer in 2007. He started off in the SQL Tools division, working on SQL Compare 7 and 8, moved onto Schema Compare for Oracle, and is now in the .NET division working on SmartAssembly.
<July 2010>
SuMoTuWeThFrSa
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567
How to Kill a Company in One Step or Save it in Three
 The majority of companies that suffer a major data loss subsequently go out of business. David Wesley... Read more...

Migrating from OCS 2007 R2 to Lync: Part 4
 Having migrated the rest of our users and legacy resources across, and start getting ready to... Read more...

Automated Script-generation with Powershell and SMO
 In the first of a series of articles on automating the process of building, modifying and copying SQL... Read more...

Seth Godin: Big in the IT Business
 Seth Godin has transformed our understanding of marketing in IT. He invented the concept of 'permission... Read more...

Using SQL Test Database Unit Testing with TeamCity Continuous Integration
 With database applications, the process of test and integration can be frustratingly slow because so... Read more...