Danger zone: calling the base version of an overriden method

Akos Nagy
Jan 30, 2017

Welcome to the danger zone:

This is something that you can do, but you shouldn't. It's fun, exciting, shows you're technically experienced and are a real .NET internals-guru type of guy - but you don't need to take unnecessary risks if you don't have to.

A while ago I was teaching a course about basic C#-.NET development and we were talking about object orientation. We were discussing polymorphism and method overriding, and I had this specific example on the screen:

public class Person
{
  public virtual void Speak()
  {
     Console.WriteLine("Blah blah");
  }
}
public class Manager : Person
{
   public override void Speak()
   {
     Console.WriteLine("Even more blah blah");
   }
}
// ...
Person p = new Manager();
p.Speak();
// ...

Nothing fancy, just to show an example of how this works. After having discussed the topic, a student asked me whether it was possible to still access the base method of the overridden from a Manager instance.

I told him what I'm telling you know: it should be possible, because if the Manager is loaded into the memory, than the Person is loaded in the memory. And from that point on, it's only a question of finding the right memory address, where the method resides. Whether this can be done with full managed programming or you have to do something crazy to get to it, I don't know. But most importantly, you never should do this: the base class is an abstraction, that you can build on. If by design a method is virtual, it means that you have the opportunity to keep it, or make it more specific — if you want to keep it, then keep it, if you want to make it more specific, make it more specific. If half the time you need the same one and half the time a more specific, then the abstraction is broken — you might be better off creating two descendant classes, keep in one of them and override in the other.

But still it got me thinking, so I did some digging — turns out you can do this in .NET (well, almost).

So I started to dig into the documentation. When you want to do something like this, there's a good chance that you will find your solution in the Reflection API. Since I was fooling around with methods, I took a look at the MethodInfo class. MethodInfo has a method called GetBaseDefinition():

When overridden in a derived class, returns the MethodInfo object for the method on the direct or indirect base class in which the method represented by this instance was first declared.

Looks promising. So I tried this:

Person obj = new Manager();
var baseMethod = typeof(Manager)
                .GetMethod("Speak").GetBaseDefinition();
baseMethod.Invoke(obj, Type.EmptyTypes);

It didn't work. I had to dig deeper — probably get the pointer to the method. Not to brag, but I had already known how to do this: get the handle for the method (.MethodHandle) and then call .GetFunctionPointer().

How had I known this? Well, previously I had had some experience with reflection and metaprogramming in the .NET (check out my GitHub repo for a runtime code generating serializer) and I had known how delegates actually work. If you try to create one via reflection, you have to specify a parameter, which normally would be a method name (more specifically, a method group) - but in fact, this is actually converted to a function pointer. Take this simple code:

class Program
{
  public void DoSomething() { }
  static void Main(string[] args)
  {
     Program p = new Program();
     Action a = new Action(p.DoSomething);
  }
}

The Main() method in IL becomes:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       21 (0x15)
  .maxstack  2
  .locals init ([0] class ConsoleApplication7.Program p,
           [1] class [mscorlib]System.Action a)
  IL_0000:  nop
  IL_0001:  newobj     instance void ConsoleApplication7.Program::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldftn      instance void ConsoleApplication7.Program::DoSomething()
  IL_000e:  newobj     instance void [mscorlib]System.Action::.ctor(object,native int)
  IL_0013:  stloc.1
  IL_0014:  ret
} // end of method Program::Main

The interesting part is the ldftn opcode. The documentation for this states that:

Pushes an unmanaged pointer (type native int) to the native code implementing a specific method onto the evaluation stack.

And from this information we can deduce that there exists such a thing as function pointer in .NET as well, and I can get it somehow. And a simple Google-search for "get function pointer .NET" reveals the GetFunctionPointer method. So there.
I tried this (created a delegate from the function pointer), and it worked!

Person obj = new Manager();
var pointer = typeof(Manager).GetMethod("Speak")
                             .GetBaseDefinition()
                             .MethodHandle
                             .GetFunctionPointer();
var baseCall = (Delegate)Activator
                     .CreateInstance(typeof(Action), obj, pointer);
baseCall.DynamicInvoke();

Now please, please do remember: this is something you shouldn't do. It's just fun to go out to the danger zone sometimes. Maybe you could add some sugar on top of this to specify with a fluent like API which method you want to call on which type. But I don't want to encourage bad design, so I won't upload that to Github :)

Akos Nagy
Posted in C# .NET