Inheritance

Perhaps the best way to describe inheritance as it is used in VB.NET is to begin with an example.

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:

Inherits CEmployee

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

Second, the keyword Overrides in the IncSalary method means that the implementation of IncSalary in CExecutive is called instead of the implementation in CEmployee. Thus, the code:

ceo.IncSalary

raises the salary of the CExecutive object ceo 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 using the Inherits keyword.

Permission to Inherit

There are two keywords used in the base class definition that affect the ability to inherit from a base class:

NotInheritable

When this is used to define a class, as in:

Public NotInheritable Class InterfaceExample

the class cannot be used as a base class.

MustInherit

When this is used to define a class, as in:

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.

Overriding

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:

Overridable

Allows but does not require a member to be overridden. Note that the default for a Public member is NotOverridable. Here is an example:

Public Overridable Sub IncSalary(  )
NotOverridable

Prohibits overriding of the member. This is the default for Public members of a class.

MustOverride

Must be overridden. When this keyword is used, the member definition is restricted to just the declaration line, with no implementation and no End Sub or End Function line. For example:

Public MustOverride Sub IncSalary(  )

Note also that when a class module contains a MustOverride member, then the class itself must be declared as MustInherit.

Overrides

Unlike the other modifiers, this modifier belongs in the derived class and indicates that the modified member is overriding a base class member. For example:

Overrides Sub IncSalary(  )

Rules of Inheritance

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 this chapter.

MyBase, MyClass, and Me

The keyword MyBase provides a reference to the base class from within a derived class. If you want to call a member of the base class from within a derived class, you can use the syntax:

MyBase.MemberName

where MemberName is the name of the member. This will resolve any ambiguity if the derived class also has a member of the same name.

The 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.New(...)

Note that 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:

MyBase.AProc

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.)

The keyword 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 IncSalary method:

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:

Me.IncSalary
MyClass.IncSalary

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 case, Me refers to an object of type Class1Derived, whereas MyClass still refers to the base class Class1 wherein the keyword MyClass appears. Thus,

Me.IncSalary

returns 12000 whereas the following:

MyClass.IncSalary

returns 10000.

Shadowing

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:

Class Statement
Constant Statement
Declare Statement
Delegate Statement
Dim Statement
Enum Statement
Event Statement
Function Statement
Interface Statement
Property Statement
Structure Statement
Sub Statement

The best way to illustrate shadowing and the differences between shadowing and overriding is with an example. Consider two classes, Class1 and Class2, where Class2 derives from Class1:

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 Overridable keyword. Class2 also defines two methods with the names TestOverride and TestShadow. Note that TestOverride is declared with the Overrides keyword, and TestShadow is declared with the Shadows 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.

Get VB.NET Language in a Nutshell, Second Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.