The classes in a given application often have relationships to one another. Consider, for instance, our Employee information application. The Employee objects in the class CEmployee represent the general aspects common to all employees — name, address, salary, and so on.
Of course, the executives of the company will have different prerequisites than, say, the secretaries. So it is reasonable to define additional classes named CExecutive and CSecretary, each with properties and methods of its own. On the other hand, an executive is also an employee, and there is no reason to define different Name properties in the two cases. This would be inefficient and wasteful.
This situation is precisely what inheritance is designed for. First, we define the CEmployee class, which implements a Salary property and an IncSalary method:
' Employee class Public Class CEmployee ' Salary property is read/write Private mdecSalary As Decimal Property Salary( ) As Decimal Get Salary = mdecSalary End Get Set mdecSalary = Value End Set End Property Public Overridable Sub IncSalary(ByVal sngPercent As Single) mdecSalary = mdecSalary * (1 + CDec(sngPercent)) End Sub End Class
Next, we define the CExecutive class:
' Executive Class Public Class CExecutive Inherits CEmployee ' Calculate salary increase based on 5% car allowance as well Overrides Sub IncSalary(ByVal sngPercent As Single) Me.Salary = Me.Salary * CDec(1.05 + sngPercent) End Sub End Class
There are two things to note here. First, the line:
indicates that the CExecutive class inherits the members of the CEmployee class. Put another way, an object of type CExecutive is also an object of type CEmployee. Thus, if we define an object of type CExecutive:
Dim ceo As New CExecutive
then we can invoke the Salary property, as in:
ceo.Salary = 1000000
raises the salary of the CExecutive object
based on a car allowance. Note also the presence of the
Overridable keyword in the definition of IncSalary
in the CEmployee class, which specifies that the class inheriting
from a base class is allowed to override the
method of the base class.
Next, we define the CSecretary class, which also inherits from CEmployee but implements a different salary increase for secretary objects:
' Secretary Class Public Class CSecretary Inherits CEmployee ' Secretaries get a 2% overtime allowance Overrides Sub IncSalary(ByVal sngPercent As Single) Me.Salary = Me.Salary * CDec(1.02 + sngPercent) End Sub End Class
We can now write code to exercise these classes:
' Define new objects Dim ThePresident As New CExecutive( ) Dim MySecretary As New CSecretary( ) ' Set the salaries ThePresident.Salary = 1000000 MySecretary.Salary = 30000 ' Set Employee to President and inc salary Debug.Writeline("Pres before: " & CStr(ThePresident.Salary)) ThePresident.IncSalary(0.4) Debug.WriteLine("Pres after: " & CStr(ThePresident.Salary)) Debug.Writeline("Sec before: " & CStr(MySecretary.Salary)) MySecretary.IncSalary(0.3) Debug.Writeline("Sec after: " & CStr(MySecretary.Salary))
The output in this case is:
Pres before: 1000000 Pres after: 1450000 Sec before: 30000 Sec after: 39600
The notion of inheritance is quite simple, as put forth in Microsoft’s documentation:
If Class B inherits from Class A, then any object of Class B is also an object of Class A and so includes the public properties and methods (that is, the public interface) of Class A. In this case, Class A is called the base class and Class B is called the derived class. On the other hand, in general, the derived class can override the implementation of a member of the base class for its own use.
We have seen in the previous example that inheritance is implemented
Public NotInheritable Class InterfaceExample
the class cannot be used as a base class.
Public MustInherit Class InterfaceExample
objects of this class cannot be created directly. Objects of a
derived class can be created, however. In other words,
MustInherit classes can be used as base classes
and only as base classes.
There are several keywords that control whether a derived class can override an implementation in the base class. These keywords are used in the declaration of the member in question, rather than in the class definition:
Public Overridable Sub IncSalary( )
Public MustOverride Sub IncSalary( )
Note also that when a class module contains a
MustOverride member, then the class itself must be
Overrides Sub IncSalary( )
In many object-oriented languages, such as C++, a class can inherit directly from more than one base class. This is referred to as multiple inheritance. VB.NET does not support multiple inheritance, and so a class can inherit directly from at most one other class. Thus, code such as the following is not permitted:
' Executive Class Public Class CExecutive 'INVALID Inherits CEmployee Inherits CWorker . . . End Class
On the other hand, Class C can inherit from Class B, which, in turn,
can inherit from Class A, thus forming an inheritance hierarchy. Note
also that a class can implement multiple interfaces through the
Interface keyword. We discuss this issue later in
MemberName is the name of the
member. This will resolve any ambiguity if the derived class also has
a member of the same name.
MyBase keyword can be used to call the
constructor of the base class in order to instantiate a member of
that class, as in:
MyBase cannot be used to call
Private class members.
Visual Basic looks for the most immediate version in parent classes of the procedure in question. Thus, if Class C derives from Class B, which derives from Class A, a call in Class C to:
first looks in Class B for a matching procedure named AProc. If none is found, then VB looks in Class A for a matching procedure. (By matching, we mean a method with the same argument signature.)
MyClass provides a reference to the
class in which the keyword is used. It is similar to the
Me keyword, except when used to call a method. To
illustrate the difference, consider a class named Class1 and a
derived class named Class1Derived. Note that each class has an
Public Class Class1 Public Overridable Function IncSalary(ByVal sSalary As Single) _ As Single IncSalary = sSalary * CSng(1.1) End Function Public Sub ShowIncSalary(ByVal sSalary As Single) MsgBox(Me.IncSalary(sSalary)) MsgBox(MyClass.IncSalary(sSalary)) End Sub End Class Public Class Class1Derived Inherits Class1 Public Overrides Function IncSalary(ByVal sSalary As Single) _ As Single IncSalary = sSalary * CSng(1.2) End Function End Class
Now consider the following code, placed in a form module:
Dim c1 As New Class1( ) Dim c2 As New Class1Derived( ) Dim c1var As Class1 c1var = c1 c1var.IncSalary(10000) ' Shows 11000, 11000 c1var = c2 c1var.IncSalary(10000) ' Shows 12000, 11000
The first call to IncSalary is made using a variable of type Class1 that refers to an object of type Class1. In this case, both of the following calls:
return the same value, because they both call IncSalary in the base class Class1.
However, in the second case, the variable of type Class1 holds a
reference to an object of the derived class, Class1Derived. In this
Me refers to an object of type
MyClass still refers to the
base class Class1 wherein the keyword
returns 12000 whereas the following:
VB.NET has a feature referred to as shadowing that is similar to overriding, but with some very important differences. Shadowing can apply to element types associated with any of the following statements:
Public Class Class1 Public x As Integer = 1 Public Overridable Sub TestOverride( ) MsgBox("Class1 method to override") End Sub Public Sub TestShadow( ) MsgBox("Class1 method to shadow") End Sub End Class Public Class Class2 ' Derived class Inherits Class1 Public Shadows x As Integer = 2 Public Overrides Sub TestOverride( ) MsgBox("Class2 method that overrides") End Sub Public Shadows Sub TestShadow( ) MsgBox("Class2 method that shadows") End Sub End Class
Class1 has two methods, TestOverride and TestShadow. Note that
TestOverride is declared with the
keyword. Class2 also defines two
methods with the names TestOverride and TestShadow. Note that
TestOverride is declared with the
keyword, and TestShadow is declared
keyword. Finally, note the presence of
a public instance field, x, in each class.
Now, consider the following test code:
Dim c2 As Class2 = New Class2( ) c2.TestOverride( ) c2.TestShadow( ) MsgBox("x=" & c2.x)
Because the object reference c2 is to an object of Class2, the calls to the TestOverride and TestShadow methods, as well as the public variable x, all refer to code in Class2, so the output messages are as expected:
Class2 method that overrides Class2 method that shadows x = 2
Now consider the code:
Dim c1 As Class1 = New Class2( ) c1.TestOverride( ) c1.TestShadow( ) MsgBox("x=" & c1.x)
Here, we have a variable of type Class1 that refers to an object of Class2. The output in this case is:
Class2 method that overrides Class1 method that shadows x = 1
To explain this, note that overriding works as follows: the method that is called is the version that is implemented not in the type (class) of the variable, but in the type (class) of the object to which that variable refers. This is a key feature of overriding and is generally referred to as a form of polymorphism. (The variable c1 takes on many forms, based on the type of object to which it refers, rather than its own type.)
On the other hand, shadowing is different from overriding: the process is not polymorphic, and so it is the type of the variable itself and not the referenced object that determines the implementation that is used. Since the variable has type Class1, the VB.NET compiler ignores the “extra goodies” that exists because c1 happens to point to a derived class object and looks only at the Class1 portion of the object, so to speak. There is no polymorphism here.
Note that member variables, such as x, can only be shadowed; they cannot be overridden.
One other difference between shadowing and overriding is that any element type in the preceding list can shadow any other element type. For instance, a method in the derived class can shadow a variable of the same name in the base class.
Unfortunately, the Microsoft documentation makes this point at the expense of the real issue, that of polymorphism. After all, it would seem to be bad programming practice to shadow elements of different types. But shadowing methods may make some sense.
Shadowing occurs in another context that is referred to as shadowing by scope . For example, if a module contains a Public variable declaration and one of the procedures within the module contains a variable declaration of the same name but perhaps a different data type, then within the procedure, the local variable will shadow the module-level variable.