Virtual methods are methods that can be overridden in inheriting (derived) classes. By default, in .NET, methods are not virtual. In order to declare a method as virtual, we need to declare it using the keyword virtual, like so:
1 2 3 4 |
public virtual void MyMethod() { Console.WriteLine("Felidae hunts " + prey); } |
In this case, any class that inherits from the class where MyMethod() is declared, can also have a method named MyMethod() with the same signature, that will override the method from the parent class. The ability to override virtual methods is a fundamental requirement for polymorphism, of which I will talk in a future lesson.
The method that overrides a virtual method from the parent class must use the keyword override,
This is how we declare the virtual method:
1 2 3 4 5 6 7 |
public class Felidae { public virtual void Hunt(object prey) { Console.WriteLine("Felidae hunts " + prey); } } |
This base class contains the virtual method Hunt(), who’s signature accepts a parameter of type object. This means that any inheriting class that wants to override this method, also needs to declare a method with same name and same signature, like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Cat : Felidae { public override void Hunt(object prey) { Console.WriteLine("Cat hunts " + prey); } } public class Lion : Felidae { public override void Hunt(object prey) { Console.WriteLine("Lion hunts " + prey); } } |
Simple explanation: we have two classes, Cat and Lion, both inheriting from the Felidae base class. They both override the method Hunt(), providing their own custom implementation and printing their personalized messages to the console. Consider the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
using System; namespace HelloWorld { class Program { static void Main(string[] args) { Cat cat = new Cat(); cat.Hunt("mice"); Lion lion = new Lion(); lion.Hunt("zebras"); Felidae felidaeLion = new Lion(); felidaeLion.Hunt("gazelles"); Console.Read(); } } public class Felidae { public virtual void Hunt(object prey) { Console.WriteLine("Felidae hunts " + prey); } } public class Cat : Felidae { public override void Hunt(object prey) { Console.WriteLine("Cat hunts " + prey); } } public class Lion : Felidae { public override void Hunt(object prey) { Console.WriteLine("Lion hunts " + prey); } } } |
which produces the following output:
In the first two instances, we expected to see the Cat and Lion implementation being printed on the console, because we overrode the virtual method of the parent class in the child classes. However, you can notice that even if the third instance was declared of type Felidae, we instantiated it of type Lion, and hence, the overridden method was called, and not the base, virtual one; the console did not print “Felidae hunts gazelles”, it printed “Lion hunts gazelles”. From this, we can conclude that virtual methods, as well as abstract methods, of which I will talk in a future lesson, can be rewritten, overridden. In fact, abstract methods are just virtual methods, but without a concrete implementation. We will also later learn that all methods defined in interfaces are abstract, and therefor virtual, even if this aspect is not explicitly defined.
Now, the logical question comes to mind: what is the use of a method in the base class, if it gets overridden in the child class? Why do we need to declare it in the first place? Well, there are two reasons for this. First of all, we might want to call the virtual method from the child class, without overriding it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
using System; namespace HelloWorld { class Program { static void Main(string[] args) { Cat cat = new Cat(); cat.Hunt("mice"); Console.Read(); } } public class Felidae { public virtual void Hunt(object prey) { Console.WriteLine("Felidae hunts " + prey); } } public class Cat : Felidae { } } |
This will produce this output:
Because we did not override the Hunt() method in the Cat class, when we called it from the instance of type Cat, we actually called the virtual method of the base class.
A second reason would be the fact that oven when overriding virtual methods in child classes, we can still explicitly call the base virtual implementation of the method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
using System; namespace HelloWorld { class Program { static void Main(string[] args) { Cat cat = new Cat(); cat.Hunt("mice"); Console.Read(); } } public class Felidae { public virtual void Hunt(object prey) { Console.WriteLine("Felidae hunts " + prey); } } public class Cat : Felidae { public override void Hunt(object prey) { Console.WriteLine("Cat hunts " + prey); base.Hunt(prey); } } } |
The console would look like this:
Because in the overridden implementation of the Hunt() method we also called
base.Hunt(prey), we told the compiler to also explicitly call the virtual implementation of the method, from the base class, while also executing the instructions in the overridden implementation. This means that if we chose to, we can supplementally add the functionality of the base implementation to the functionality of the overridden implementation, effectively executing both.
This is the primary difference between an override method and a normal method. The first one allows us to change/complement/alter some of the inherited functionality. A simple example is the virtual method ToString(), that any .NET object has. Since it is declared as virtual, we can override it in any implementation, providing custom functionality. I can demonstrate this with the following simple example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
using System; namespace HelloWorld { class Program { static void Main(string[] args) { Cat cat = new Cat(); Console.WriteLine(cat.ToString()); Console.Read(); } } public class Cat { } } |
The output would be this one:
which is not a very descriptive output. Instead, I can override this default virtual implementation with a custom one, providing a much more humanly readable output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
using System; namespace HelloWorld { class Program { static void Main(string[] args) { Cat cat = new Cat(); Console.WriteLine(cat.ToString()); Console.Read(); } } public class Cat { public override string ToString() { return "I am a cat!"; } } } |
and the console would print this:
You have to admit, this is a much nicer way of outputting an object.
One final aspect of virtual methods: if we do not want our overridden methods to be further altered by other classes that would continue to inherit our class, or if we want to stop our child class from being further inherited by other classes, we can use the keyword sealed. This keyword basically says “this class/property/method CANNOT be inherited”. In this light, it makes no sense to mark a virtual method as sealed. The override keyword tells the compiler that the method can be overridden by child objects, but the sealed keyword says that the method cannot be inherited by a child class in the first place. In fact, when used on methods or properties, the sealed keyword is only allowed on overriding methods or properties. In that case, the keyword sealed means that the overridden method or property cannot be further inherited/modified by classes inheriting the class where the overriding method is declared:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public class BaseClass { public virtual void SomeMethod() { } } public class MyDerivedClass : BaseClass { public void AnotherMethod() { // there's no point sealing this guy - it's not virtual } public override sealed void SomeMethod() { // If I don't seal this guy, then another class derived from me can override again } } public class GrandChildClass : MyDerivedClass { public override void AnotherMethod() { // ERROR - AnotherMethod isn't virtual } public override void SomeMethod() { // ERROR - we sealed SomeMethod in MyDerivedClass // If we hadn't - this would be perfectly fine } } |
In the next lesson, I will discuss the abstract concept of programming.
Tags: methods, methods declaration, object oriented programming, OOP, operators, other operators, override, virtual
Nice to see new articles here !
As much as my time allows 🙂