Common Type System—Virtuals and Overriding
Virtuals and Overriding
By default, subclasses inherit both nonprivate method interfaces and implementations of their base
classes (all the way up the chain). A method can be marked as virtual, however, which declares that
further derived types can override the behavior of the base type. Overriding a virtual method simply
replaces the implementation inherited from the base type.
Some languages like Java treat all methods as virtual by default. C# does not, meaning that you must opt
in to virtualism explicitly using the virtual keyword.
class Base { public virtual void Foo() { Console.WriteLine(“Base::Foo”); } } class Derived : Base { public override void Foo() { Console.WriteLine(“Derived::Foo”); } }
Derived has overridden the method Foo, supplying its own implementation. When a call is made to the
virtual method Base::Foo, the method implementation is selected at runtime based on the identity the
instance against which the call is made. For example, consider the following sample:
// Construct a bunch of instances: Base base = new Base(); Derived derived = new Derived(); Base derivedTypedAsBase = new Derived(); // Now invoke Foo on each of them: base.Foo(); derived.Foo(); derivedTypedAsBase.Foo();
When run, the above snippet of code will output the following to the console:
Base::Foo Derived::Foo Derived::Foo
To demonstrate that no analysis trickery accomplishes this, consider this method:
public void DoSomething(Base b) { b.Foo(); } // In another assembly altogether... DoSomething(new Base()); DoSomething(new Derived());
This prints out the following:
Base::Foo Derived::Foo
Virtual method dispatch traverses the object reference to get at the method table of the instance. (We described object layout earlier in this chapter; if you’ve forgotten, you might want to refer back.) The method table contains a set of method slots, each of which is a pointer to a piece of code. When performing a virtual method dispatch, the pointer to the object is dereferenced, and then the pointer to the appropriate virtual method slot in the method table; then the value held in the slot is used as the target address of the method call. Please refer to Chapter 3 for more information about virtual method dispatching, in particular how the JIT Compiler plays a role.
|

