Explicit Method Overriding
Thus far, I’ve discussed implicit virtual method overriding—that is, a virtual method defined in a class overriding another virtual method of the same name and signature, defined in the class’s ancestor or an interface the class implements. But implicit overriding covers only the simplest case.
Consider the following problem: class A implements interfaces IX and IY, and each of these interfaces defines its own virtual method int32 Foo(int32). It is known that these methods are different and must be implemented separately. Implicit overriding can’t help in this situation. It’s time to use the MethodImpl metadata table.
The MethodImpl metadata table contains descriptors of explicit method overrides. An explicit override states which method overrides which other method. To define an explicit override in ILAsm, the following directive is used within the scope of the overriding method:
.override <class_ref>::<method_name>
The signature of the method need not be specified because the signature of the overriding method must match the signature of the overridden method, and the signature of the overriding method is known: it’s the signature of the current method. For example:
.class public interface IX { .method public abstract virtual int32 Foo(int32) { } } .class public interface IY { .method public abstract virtual int32 Foo(int32) { } } .class public A implements IX,IY { .method public virtual int32 XFoo(int32) { .override IX::Foo } .method public virtual int32 YFoo(int32) { .override IY::Foo } }
Not surprisingly, we can’t override the same method with two different methods within the same class: there is only one slot in the v-table to be overridden. However, we can use the same method to override several virtual methods. Let’s have a look at the following code from the sample file Override.il on the companion CD:
.class public A { .method public specialname void .ctor() { ret } .method public void Foo() { ldstr "A::Foo" call void [mscorlib]System.Console::WriteLine(string) ret } .method public virtual void Bar() { ldstr "A::Bar" call void [mscorlib]System.Console::WriteLine(string) ret } .method public virtual void Baz() { ldstr "A::Baz" call void [mscorlib]System.Console::WriteLine(string) ret } } .class public B extends A { .method public specialname void .ctor() { ret } .method public void Foo() { ldstr "B::Foo" call void [mscorlib]System.Console::WriteLine(string) ret } .method public virtual void BarBaz() { .override A::Bar .override A::Baz ldstr "B::BarBaz" call void [mscorlib]System.Console::WriteLine(string) ret } } .method public static void Exec() { .entrypoint newobj instance void B::.ctor() // Create instance of derived class castclass class A // Cast it to base class dup // We need 3 instance pointers dup // On stack for 3 calls call instance void A::Foo() callvirt instance void A::Bar() callvirt instance void A::Baz() ret}
The output of this code demonstrates that the method B::BarBaz overrides both A::Bar and A::Baz:
A::Foo B::BarBaz B::BarBaz
Virtual method overriding, both implicit and explicit, is propagated to the descendants of the overriding class, unless the descendants themselves override those methods. The second half of the sample file Override.il demonstrates this:
.class public C extends B { .method public specialname void .ctor() { ret } // No overrides; let's inherit everything from B } .method public static void Exec() { .entrypoint newobj instance void C::.ctor() // Create instance of derived class castclass class A // Cast it to "grandparent" dup // We need 3 instance pointers dup // On stack for 3 calls call instance void A::Foo() callvirt instance void A::Bar() callvirt instance void A::Baz() Ret }
The output is the same, which proves that class C has inherited the overridden methods from class B:
A::Foo B::BarBaz B::BarBaz
ILAsm supports an extended form of the explicit override directive, placed within the class scope:
.override <class_ref>::<method_name> with <method_ref>
For example, the overriding effect would be the same in the preceding code if we defined class B like so:
.class public B extends A { .method public specialname void .ctor() { ret } .method public void Foo() { ldstr "B::Foo" call void [mscorlib]System.Console::WriteLine(string) ret } .method public virtual void BarBaz() { ldstr "B::BarBaz" call void [mscorlib]System.Console::WriteLine(string) ret } .override A::Bar with instance void B::BarBaz() .override A::Baz with instance void B::BarBaz() }
In the extended form of the .override directive, the overriding method must be fully specified because the extended form is used within the overriding class scope, not within the overriding method scope.
To tell the truth, the extended form of the .override directive is not very useful in the first version of the common language runtime because the overriding methods are restricted to those of the overriding class. Under these circumstances, the short form of the directive is sufficient, and I doubt that anyone would want to use the more cumbersome extended form. But I’ve noticed that in this industry the circumstances tend to change.