While the OO features of VB have been very powerful and useful, we have been held back in many cases by the lack of inheritance in the language. Inheritance is the ability of a class to gain the interface and behaviors of an existing class. The process by which this is accomplished is called subclassing. When we create a new class that inherits the interface and behaviors from an existing class, we have created a subclass of the original class. This is also known as an “is-a” relationship, where the new class “is-a” type of original class.
There is a lot of terminology surrounding inheritance – much of it redundant. The original class, from which we inherit interface and behavior is known by the following interchangeable terms:
- Parent class
- Base class
The new class that inherits the interface and behaviors is known by the following interchangeable terms:
- Child class
Inheritance is also sometimes called generalization. In fact this is the term used within the Universal Modeling Language (UML) – the most commonly used object diagramming notation.
Inheritance is often viewed through the lens of biology, where, for example, a dog is a canine and a canine is a mammal. Hence, by being a canine, a dog inherits all the attributes and behavior of a mammal. While useful for visualization, these analogies only go so far.
For interested object-oriented aficionados, VB.NET does not allow multiple inheritance – where a subclass is created by inheriting from more than one parent class. This feature is not supported by the .NET runtime and thus is not available from VB.NET. VB.NET does allow deep inheritance hierarchies where a class is subclassed from a class that is subclassed, but it doesn’t allow a class to be subclassed from multiple parent classes all at once.
We can contrast inheritance, an “is-a” relationship, with another type of parent-child relationship – the “has-a” relationship. This is also known as aggregation or containment.
In a “has-a” relationship, the parent object owns one or more of the child objects, but the child objects are of different types from the parent. For instance, an Invoice has-a LineItem. The LineItem object isn’t subclassed from Invoice – it is an entirely different class that just happens to be owned by the Invoice parent.
This distinction is important, because the terms parent and child are used frequently when working with objects – sometimes when referring to inheritance and sometimes when referring to aggregation. It is important to understand which is which or things can get very confusing.
Within this section, we’ll use the terms parent, child, and subclass – all in the context of inheritance.
Implementing Basic Inheritance
To explore inheritance, consider a business
example with a sales order that has line items. We might have product line items
and service line items. Both are examples of line items, but both are somewhat
different as well. While we could certainly implement
classes separately, they’d have a lot of common code between them. Redundant
code is hard to maintain, so it would be nicer if they could somehow directly
share the common code between them.
This is where inheritance comes into play.
Using inheritance, we can create a
class that contains all the code common to any sort of line item. Then we can
classes that inherit from
– thus automatically gaining all the common code – including interface and implementation
in an OO form.
class might appear as:
This class has things common to any line item – some basic data fields and a method to calculate the cost of the item.
If a line item is for a product, however,
we might have additional requirements. The
value should probably be validated to make sure it refers to a real product,
and perhaps we want to provide a product description as well:
Note the use of the
It is this statement that causes the
class to gain all the interface elements and behaviors from the
class. This means that we can have client code like this:
This code makes use of both the
property (from the
class) and the
property from the
class. Both are equally part of the
class, since it is a subclass of
Likewise, a line item for a service might have a date for when the service was provided, but otherwise be the same as any other line item:
Again, notice the use of the Inherits
statement that indicates this is a subclass of the
property is simply added to the interface gained from the
By default any
class we create can be used as a base class from which other classes can be
created. There are times when we might want to create a class that cannot be
subclassed. To do this we can use the
keyword in our class declaration:
When this keyword is used, no other code
may use the
keyword to create a subclass of our class.
Inheritance and Scoping
we create a subclass through inheritance, the new class gains all the
methods, properties, and variables from the original class. Anything declared
in the original class will not be directly available to our code in the new
The exception to this is the
method. Constructor methods must be re-implemented in each subclass. We’ll discuss
this in more detail later in the chapter.
For instance, we might rewrite the
methods from the
With this change, we can see that the
makes use of a
method to do its work.
When we subclass
to create the
object will have an
method because it is declared as
in the base class. The
method, on the other hand, is declared as
and so neither the
class nor any client code will have any access to it.
Does this mean the
method will break when called through the
object? Not at all. Since the
method’s code resides in the
class, it has access to the
method even though the
class can’t see the method.
For instance, in our client code we might have something like this:
The result is displayed in a message box,
thus illustrating that the
method was called on our behalf even though neither our client code, nor the
code directly made the call.
Sometimes Public and Private aren’t enough. If we declare something as Private it is totally restricted to our class, while if we declare something as Public (or Friend) it is available to both subclasses and client code. There are times when it would be nice to create a method that is available to subclasses, but not to client code.
This is where the
scope comes into play. When something is declared as
it is not available to any code outside of the class. However, it is
available to classes that are derived from our class through inheritance.
Here we have a parent class with a
This variable is not available to any client code. However, the variable is
fully available to any code within
because it inherits from the parent.
In this example,
method that actually does return the protected value – but the variable
is not directly available to any client code (that is, code outside the class).
key attribute of inheritance is that a subclass not only gains the behaviors
of the original class, but it can also override those behaviors. We’ve already
seen how a subclass can extend the original class by adding new
methods. However, by using the concept of overriding, a subclass can alter
the behaviors of methods that were declared on the parent class.
By default, methods cannot be overridden
by a subclass. To allow them to be overridden, the parent class must declare
the method using the
We can also explicitly disallow overriding
through the use of the
Of course since this is the default, this keyword
is rarely used.
However, it may be a good practice to explicitly define whether a method can or cannot be overridden to increase the clarity of code and to protect against the possibility that the default behavior might someday change.
If we then create a subclass, we can optionally
override the behavior of
by using the
Now we can write client code such as:
The result will be a message dialog containing
the text Hello
from SubClass. This isn’t surprising – after all,
we overrode the
method with our new code.
However, consider the following client code:
First off, it seems odd to declare a variable
but then create a
object instead. This is perfectly acceptable however – it is yet another way
of implementing polymorphism. Since
variable can hold a reference to a
This is true in general. When using inheritance, a variable of the parent type can always hold references to any child type created from the parent.
What may be more surprising is the message that is displayed in our message box when this code is run. The message we see is Hello from SubClass.
How can this be? The variable is declared
– shouldn’t the
implementation be called? The reason the
implementation from the child class is called is that the method is virtual.
The concept of a virtual method is such that the “bottom-most” implementation
of the method is always used in favor of the parent implementation – regardless
of the data type of the variable being used in the client code.
The term “bottom-most” comes from the typical way a chain of inherited objects is diagrammed. Usually the parent class is displayed, with the subclasses underneath. If the subclasses are also subclassed, then those classes are shown even further down. This is illustrated by the following UML diagram:
Regardless of the variable data
type, the implementation of
will be invoked based on the actual class we use to create the object. In our
previous example, we created an object of type
thus ensuring that the
implementation in that class would be invoked. This is illustrated by the following
diagram, which shows how the method calls are made:
If we create an object from type
with the following code:
implementation in the
class will be invoked since that is the type of object we created as shown in
the following diagram:
We can also create the object from the
In this case, the class doesn’t directly
so we start looking back up the inheritance chain:
The first class up that chain is
which does have an implementation – so it is that implementation which
is invoked. No code from the
class is invoked at all.
keyword is used any time we want our code to refer to methods within the current
object. This was used in VB6 – when we might have utilized the
keyword to refer to the current form, or the current instance of an object –
and the same is true in VB.NET.
keyword is usually optional, since any method call is assumed to refer to the
current object unless explicitly noted otherwise. The exception is when we are
working with shadowed
A shadowed variable is a procedure-level variable with the same name as a class-level variable. For instance:
is declared at the class level and within the
method. Within that method only the local, or shadowed, variable is used unless
we explicitly reference the class-level variable with the
Here we can see that
can be used to reference the local variable, while
can be used to reference the class-level variable.
As useful as the
keyword can be for referring to the current object, when we start working with
inheritance, it isn’t enough to do everything we want.
There are two issues we need to deal with. Sometimes we may want to explicitly call into our parent class – and sometimes we may want to ensure that the code in our class is being called, rather than the code in some subclass that has inherited our code.
At times we might want to explicitly call into methods in our parent class. Remember that when we override a method it is virtual – so the method call will invoke the “bottom-most” implementation – not the parent implementation. However, sometimes we might need that parent implementation.
To invoke the parent class from within
our subclass we can use the
keyword. For instance:
If we run our client code now, we’ll get two message boxes. First we’ll get the message from the subclass, followed by the one from the parent class.
keyword can be used to invoke or use any
element from the parent class. This includes all of those elements directly
on the base class, and also any elements the base class inherited from other
classes higher in the inheritance chain.
only refers to the immediate parent of the current class. If we create
that inherits from
keyword would refer to the
code, not the
code. There is no direct way to navigate more than one level up the inheritance
A more complex scenario is one where the code in our class might end up invoking code from other classes created from our class.
When we create a class, we’ll frequently make calls from within our class to other methods within that same class. This occurs in the following code:
In this case, the
method calls the
method to do some work. Notice however, that
is marked as
so a subclass might provide a different implementation for the method. For example:
As we discussed earlier, VB.NET methods
are virtual – which means that an object of type
will always invoke
rather than from the
class. This is true even for code in the
class itself – so when the
method calls the
method it will invoke the overridden implementation in
This can be illustrated by the following client code:
We will see a dialog displaying SubClass
other stuff. The
method in the
class invokes the overridden
implementation within the
If we don’t want this behavior – if we
want the code in the
class to know for certain that it is calling the
– we need to use the
We can’t use the
keyword, because that will reference the virtual method.
on the other hand, forces the call to be handled by the code in the same class
as the call – in this case the
This example is illustrated by the following diagram, which shows the method calls being made:
Here we can see that the client calls
which is actually invoked from the
class then calls
but since it is implemented in
that is the implementation that is invoked.
Overriding the Constructor Method
already seen how we can override methods, and how to use the
keywords to interact with the various overridden methods in our inheritance
chain. However, there are special rules that govern the process of overriding
New methods aren’t automatically carried from a parent to a subclass like normal methods. Each subclass must define its own constructors, though those constructors may call into the parent class using the MyBase keyword:
When calling the base class constructor, that call must be the first line in our constructor code – anything else is an error. This is totally optional, however, since the constructor of the parent class is automatically called on our behalf before our constructor code begins to run, unless we make that call manually.
As we discussed earlier, the
method can be overloaded, providing various implementations. If our parent class
provides alternate implementations of
we may want to manually make the call in order to cause the correct implementation
to be called, based on our requirements.
Creating Base Classes and Abstract Methods
So far, we’ve seen how to inherit from a class, how to overload and override methods, and how virtual methods work. In all of our examples so far, the parent classes have been useful in their own right. Sometimes however, we want to create a class such that it can only be used as a base class for inheritance.
our original sales order line item example, it may make little sense for anyone
to create an object based on the generic
class. In fact, we may want to ensure that only more specific subclasses derived
can be created. What we want to create is something called a base
class. This is done using the
keyword in the class declaration:
Typically, no other change is required
in our class. The result of this keyword is that it is no longer possible to
write client code that creates an instance of the
class, so the following would cause a syntax error:
to use the code in the LineItem
class, we must create subclasses and use those throughout
we have is to create a method that must be overridden by a subclass. We might
want to do this when we are creating a base class that provides some behavior,
but relies on subclasses to also provide some behavior in order to function
properly. This is accomplished by using the
keyword on a method declaration:
Notice that there is no
Sub or any other code associated with the method.
we cannot provide any implementation for the method in our class. Such
a method is called an abstract
method or pure
virtual function, since it only defines the interface
and no implementation.
Methods declared in this manner must be overridden in any subclass that inherits from our base class. If we don’t override one of these methods, we’ll generate a syntax error in the subclass and it won’t compile.
Abstract Base Classes
can combine these two concepts – using both
– to create something called an abstract
base class. This is a class that provides no implementation,
only the interface definitions from which a subclass can be created. An example
might be as follows:
This technique can be very useful when
creating frameworks or the high-level conceptual elements of a system.
class that inherits
must implement both
or a syntax error will result.
In some ways an abstract base class is
very comparable to defining an interface using the
keyword. We’ll discuss the
keyword in detail later in this chapter. For now, be aware that the
keyword is used to formally declare an interface that can be implemented using
keyword as in VB6.
We could define the same interface as shown in this example with the following code:
Any class that implements the IParent interface must implement both DoSomething and DoOtherStuff or a syntax error will result – and in that regard this technique is similar to an abstract base class.
There are differences however. In particular,
when we create a new class by subclassing the
class, that class can in turn be subclassed. Those classes will automatically
methods due to the nature of inheritance.
Compare this with the interface approach,
where each individual class must independently implement the
interface and provide its own implementation of the two methods. If we
never intend to reuse the code that implements these methods as we create new
classes then the interface approach is fine, but if we want code reuse within
subclasses inheritance is the way to go.