Override versus Hide (static binding versus dynamic binding)
Background This is a topic repeatedly asked about both online and offline. This entry explains the following OOPs concepts to help clear them:
- Method overriding and method hiding (implemented using the override and new keywords in C# and Overrides and Shadows keywords in VB.NET).
- Static Binding and Dynamic Binding in this context.
- Other related concepts
Static Binding
When a reference type variable is initialized, the reference may hold either:
- an object of the same type as that of the reference type
- an object of a type related to the reference type
1
2 public class Base
3 {
4 public void Print()
5 {
6 Console.WriteLine("Base");
7 }
8 }
9
10 public class Derv : Base
11 {
12 public void Print()
13 {
14 Console.WriteLine("Derv");
15 }
16 }
17
The code below is perfectly legal in this context:
1
2 Base obj;
3
4 obj = new Base();
5
6 // This is possible since Derv is related to Base
7 obj = new Derv();
8
In such a scenario, when a method is invoked against a reference variable, the actual method going to be called depends upon the type of the reference. For example:
1
2 Base obj;
3 Derv der;
4
5 obj = new Base();
6 obj.Print(); // Output: 'Base'
7
8 obj = new Derv();
9 obj.Print(); // Output: 'Base'
10
11 der = new Derv();
12 der.Print(); // Output: 'Derv'
13
At line number 9, the compiler determines which method to call (the Base.Print method or Derv.Print method) by checking the type of the reference variable obj. Since the reference variable obj (not the actual object stored at the reference) happens to be of type Base, the compiler calls Base.Print. In a similar manner, at line number 12, the compiler invokes Derv.Print as the reference variable der is of type Derv.
This is known as Static Binding because the compiler does all this at compile time while building the final IL code, which is thus static (unchangeable at run-time).
Method Hiding
The type Derv derives from Base and thus inherits the Base.Print method. But note that Derv itself defines a Print method. When a derived class defines a method with the same name as that of a method in the base class, the base class’s method would never be invoked if the type of reference variable is that of the derived class. In the previous code snippet at line number 12, though Derv inherits the Base.Print method, it was Derv.Print that was invoked. Here, Derv.Print is said to be hiding Base.Print.
In order to make sure that this is indeed what the developer requires, the compiler would issue a warning saying that Derv.Print is hiding Base.Print. The compiler simply wants to bring to the attention of the programmer that there exists a method with the same name in the base class, and that the derived class’s method is hiding it. To get rid of the warning, we need to use the new keyword:
1
2 public class Derv : Base
3 {
4 public new void Print()
5 {
6 Console.WriteLine("Derv");
7 }
8 }
9
This reassures the compiler that the programmer knows about this issue, and that this implementation is purposeful.
Method Overriding and Dynamic Binding
By using the virtual and override keywords, you could control which method is invoked in an inheritance scenario.
1
2 public class Base
3 {
4 public virtual void Print()
5 {
6 Console.WriteLine("Base");
7 }
8 }
9
10 public class Derv : Base
11 {
12 public override void Print()
13 {
14 Console.WriteLine("Derv");
15 }
16 }
17
This definition would yield the following result.
1
2 Base obj;
3
4 obj = new Base();
5 obj.Print(); // Output: 'Base'
6
7 obj = new Derv();
8 obj.Print(); // Output: 'Derv'
9
Note that at line number 8, even though the reference variable obj was of type Base, the method that was invoked was Derv.Print rather than Base.Print. This is because of the following reasons:
- Base.Print declares itself as virtual, which means derived classes may override this method
- Derv.Print explicitly states that this method overrides the Base.Print method, by using the override keyword
Thus, when the Print method is invoked, the derived type’s method gets invoked.
In addition to Derv, there could be more types which derive from Base, or even from Derv or any of its subclasses. Consequently, at any point of time, the reference variable obj could be holding any object instance whose type is in some way related to the type Base. It is thus not possible to find out what is the actual type of the instance held in obj until runtime. Hence, binding happens at runtime when the actual object instance exists, and the method is then invoked dynamically. This is known as Dynamic Binding.
Remarks
If Derv.Print was not declared with the override keyword, Derv.Print would be ignored and Base.Print would be invoked. This design ensures that the programmer explicitly requests for overriding. Conversely, if Base.Print was not declared as virtual, Derv.Print will not be able to override that method; in other words, the base class developer should explicitly permit derived class developers to override its methods. This design prevents any accidental invocation of the derived class’s method when a new version of the base class with a method of the same name ships.