Something which I feel carries a lot of confusion in the .NET realm is virtual methods. During interviews, I tend to ask candidates about virtual methods: why and when they’d use one, what the purposes is, how a virtual method “works” under the hood, and how it differs from “shadowing”. Surprisingly, in what has probably been over one hundred interviews with senior-ish candidates, I don’t believe that more than one or two of them have answered anything about virtual methods correctly. From this I conclude that the understanding of virtual methods is not strong among the typical developer… And so, let us dive in.
Virtual methods exist to allow you to not just override, but completely replace functionality within a derivation hierarchy. In other words, you can change the functionality of a less derived class from within a more derived class. This is different from shadowing in that shadowing overrides the functionality of a method for the given derived class only and not the entire class hierarchy.
Shadowing is accomplished by declaring the same non-virtual method which exists in the base class, while adding the “new” keyword to indicate that shadowing behaviour was intended.
Overriding is accomplished by declaring the same virtual method which exists in the base class, while adding the “override” keyword to indicate that overriding behaviour was intended.
A code sample will make these differences very clear. Let us define a base Vehicle class (as abstract so that it cannot be instantiated) and a deriving Motorcycle class. Both will output information to the Console about the number of wheels that they have:
/// <summary>
/// Represents a Vehicle.
/// </summary>
public abstract class Vehicle
{
/// <summary>
/// Prints the Number of Wheels to the Console.
/// Virtual so can be changed by more derived types.
/// </summary>
public virtual void VirtualPrintNumberOfWheels()
{
Console.WriteLine("Number of Wheels: 4");
}
/// <summary>
/// Prints the Number of Wheels to the Console.
/// </summary>
public void ShadowPrintNumberOfWheels()
{
Console.WriteLine("Number of Wheels: 4");
}
}
/// <summary>
/// Represents a Motorcycle.
/// </summary>
public class Motorcycle : Vehicle
{
/// <summary>
/// Prints the Number of Wheels to the Console.
/// Overrides base method.
/// </summary>
public override void VirtualPrintNumberOfWheels()
{
Console.WriteLine("Number of Wheels: 2");
}
/// <summary>
/// Prints the Number of Wheels to the Console.
/// Shadows base method.
/// </summary>
public new void ShadowPrintNumberOfWheels()
{
Console.WriteLine("Number of Wheels: 2");
}
}
Above we’ve defined two classes: an abstract base Vehicle class which has a virtual and non-virtual method which both do the same thing, and a Motorcycle class which implements the Vehicle class while overriding the virtual method and shadowing the normal method. Now we will call the methods with different Type signatures to see the differences:
static void Main(string[] args)
{
// Instantiate a Motorcycle as type Motorcycle
Motorcycle vehicle = new Motorcycle();
Console.WriteLine("Calling Shadow on Motorcycle as Type Motorcycle");
vehicle.ShadowPrintNumberOfWheels();
Console.WriteLine("Calling Virtual on Motorcycle as Type Motorcycle");
vehicle.VirtualPrintNumberOfWheels();
// Instantiate a Motorcycle as type Vehicle
Vehicle otherVehicle = new Motorcycle();
Console.WriteLine("Calling Shadow on Motorcycle as Type Vehicle");
otherVehicle.ShadowPrintNumberOfWheels();
Console.WriteLine("Calling Virtual on Motorcycle as Type Vehicle");
otherVehicle.VirtualPrintNumberOfWheels();
Console.ReadKey();
}
Before we reveal the results, ask yourself: what do you expect the outcomes for each call to be? You may be surprised. And now, the results:
Were your assumptions about the code correct? The virtual method’s override has changed the functionality within the base Vehicle class as well. Therefore, it should be clear to see that virtual methods should be used whenever you wish to change not just the functionality of a method within your currently derived class, but within all deriving and base classes.
Finally, I’d like to address one more constant “debate”. You will undoubtedly hear repeatedly in your career that virtual methods are very expensive to override and use. This is because of the fact that the actual instance method to call can only be determined at runtime, and not compile time. This is an old wives tale that has very little basis in reality (due to the intelligent use of “vtables”): virtual methods have little to no additional cost over regular methods..