New Object-Oriented Capabilities in VB.NET

Inheritance

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
  • Superclass
  • Base class

The new class that inherits the interface and behaviors is known by the following interchangeable terms:

  • Child class
  • Subclass

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 ProductLine and ServiceLine 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 LineItem class that contains all the code common to any sort of line item. Then we can create ProductLine and ServiceLine classes that inherit from LineItem – thus automatically gaining all the common code – including interface and implementation in an OO form.

A simple LineItem class might appear as:

Public Class LineItem
  Private mintID As Integer
  Private mstrItem As String
  Private msngPrice As Single
  Private mintQuantity As Integer
  Public Property ID() As Integer
    Get
      Return mintID
    End Get
    Set
      mintID = value
    End Set
  End Property
  Public Property Item() As String
    Get
      Return mstrItem
    End Get
    Set
      mstrItem = Value
    End Set
  End Property
  Public Property Price() As Single
    Get
      Return msngPrice
    End Get
    Set
      msngPrice = Value
    End Set
  End Property
  Public Property Quantity() As Integer
    Get
      Return mintQuantity
    End Get
    Set
      mintQuantity = Value
    End Set
  End Property
  Public Function Amount() As Single
    Return mintQuantity * msngPrice
  End Function
End Class

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 Item 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:

Public Class ProductLine
  Inherits LineItem
  Private mstrDescription As String
  Public ReadOnly Property Description() As String
    Get
      Return mstrDescription
    End Get
  End Property
  Public Sub New(ByVal ProductID As String)
    Item = ProductID
    ‘ load product data from database
    mstrDescription = “Test product description”
  End Sub
End Class

Note the use of the Inherits statement.

  Inherits LineItem

It is this statement that causes the ProductLine class to gain all the interface elements and behaviors from the LineItem class. This means that we can have client code like this:

Protected Sub Button1_Click(ByVal sender As Object, _
    ByVal e As System.EventArgs)
  Dim pl As ProductLine
  pl = New ProductLine(“123abc”)
  MessageBox.Show(pl.Item)
  MessageBox.Show(pl.Description)
End Sub

This code makes use of both the Item property (from the LineItem class) and the Description property from the ProductLine class. Both are equally part of the ProductLine class, since it is a subclass of LineItem.

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:

Public Class ServiceLine
  Inherits LineItem
  Private mdtDateProvided As Date
  Public Sub New()
    Quantity = 1
  End Sub
  Public Property DateProvided() As Date
    Get
      Return mdtDateProvided
    End Get
    Set
      mdtDateProvided = Value
    End Set
  End Property
End Class

Again, notice the use of the Inherits statement that indicates this is a subclass of the LineItem class. The DateProvided property is simply added to the interface gained from the LineItem class.

Preventing Inheritance

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 NotInheritable keyword in our class declaration:

Public NotInheritable Class ProductLine
End Class

When this keyword is used, no other code may use the Inherits keyword to create a subclass of our class.

Inheritance and Scoping

When we create a subclass through inheritance, the new class gains all the Public and Friend methods, properties, and variables from the original class. Anything declared as Private in the original class will not be directly available to our code in the new subclass.

The exception to this is the New 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 Amount methods from the LineItem class slightly:

Public Function Amount() As Single
  Return CalcAmount
End Function
Private Function CalcAmount() As Single
  Return fQuantity * fPrice
End Function

With this change, we can see that the Public method Amount makes use of a Private method to do its work.

When we subclass LineItem to create the ServiceLine class, any ServiceLine object will have an Amount method because it is declared as Public in the base class. The CalcAmount method, on the other hand, is declared as Private and so neither the ServiceLine class nor any client code will have any access to it.

Does this mean the Amount method will break when called through the ServiceLine object? Not at all. Since the Amount method’s code resides in the LineItem class, it has access to the CalcAmount method even though the ServiceLine class can’t see the method.

For instance, in our client code we might have something like this:

Protected Sub Button1_Click(ByVal sender As Object, _
    ByVal e As System.EventArgs)
  Dim sl As ServiceLine
  sl = New ServiceLine()
  sl.Item = “delivery”
  sl.Price = 20
  sl.DateProvided = Now
  MsgBox(sl.Amount, MsgBoxStyle.Information, “Amount”)
End Sub

The result is displayed in a message box, thus illustrating that the CalcAmount method was called on our behalf even though neither our client code, nor the ServiceLine code directly made the call.

Protected Methods

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 Protected scope comes into play. When something is declared as Protected, 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.

For example:

Public Class ParentClass
  Protected TheValue As Integer
End Class
Public Class SubClass
  Inherits ParentClass
  Public Function GetValue() As Integer
    Return TheValue
  End Function
End Class

Here we have a parent class with a Protected member – TheValue. This variable is not available to any client code. However, the variable is fully available to any code within SubClass, because it inherits from the parent.

In this example, SubClass has a Public method that actually does return the protected value – but the variable TheValue is not directly available to any client code (that is, code outside the class).

Overriding Methods

One 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 Public, Protected, and Friend 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 Overridable keyword:

Public Class Parent
  Public Overridable Sub DoSomething()
    MessageBox.Show(“Hello from Parent”)
  End Sub
End Class

We can also explicitly disallow overriding through the use of the NotOverridable keyword. 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 DoSomething by using the Overrides keyword:

Public Class SubClass
  Inherits Parent
  Public Overrides Sub DoSomething()
    MessageBox.Show(“Hello from SubClass”)
  End Sub
End Class

Now we can write client code such as:

Dim obj As New SubClass()
obj.DoSomething()

The result will be a message dialog containing the text Hello from SubClass. This isn’t surprising – after all, we overrode the DoSomething method with our new code.

Virtual Methods

However, consider the following client code:

Dim obj As Parent
obj = New SubClass()
obj.DoSomething()

First off, it seems odd to declare a variable of type Parent, but then create a SubClass object instead. This is perfectly acceptable however – it is yet another way of implementing polymorphism. Since SubClass “is-a” Parent, any ParentorSubClass variable can hold a reference to a SubClass object.

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 as type Parent – shouldn’t the Parent implementation be called? The reason the DoSomething 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.

Unlike many object-oriented languages, all methods in VB.NET are virtual.

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 DoSomething will be invoked based on the actual class we use to create the object. In our previous example, we created an object of type SubClass, thus ensuring that the DoSomething 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 Parent with the following code:

Dim obj As Parent
obj = New Parent()
obj.DoSomething()

The DoSomething implementation in the Parent 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 SubSubClass class:

Dim obj As Parent
obj = New SubSubClass()
obj.DoSomething()

In this case, the class doesn’t directly implement DoSomething, so we start looking back up the inheritance chain:

The first class up that chain is SubClass, which does have an implementation – so it is that implementation which is invoked. No code from the Parent class is invoked at all.

Me Keyword

The Me 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 Me keyword to refer to the current form, or the current instance of an object – and the same is true in VB.NET.

The Me keyword is analogous to the this keyword in C++ and C# languages.

The Me 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 variables.

A shadowed variable is a procedure-level variable with the same name as a class-level variable. For instance:

Public Class TheClass
  Private strName As String
  Public Sub DoSomething()
    Dim strName As String
    strName = “Fred”
  End Sub
End Class

The variable strName is declared at the class level and within the DoSomething method. Within that method only the local, or shadowed, variable is used unless we explicitly reference the class-level variable with the Me keyword:

  Public Sub DoSomething()
    Dim strName As String
    strName = “Fred”      ‘ sets the local variable’s value
    Me.strName = “Mary”   ‘ sets the class level variable’s value
  End Sub

Here we can see that strName can be used to reference the local variable, while Me.strName can be used to reference the class-level variable.

As useful as the Me 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.

MyBase Keyword

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 MyBase keyword. For instance:

Public Class SubClass
  Inherits Parent
  Public Overrides Sub DoSomething()
    MessageBox.Show(“Hello from subclass”)
    MyBase.DoSomething()
  End Sub
End Class

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.

The MyBase keyword can be used to invoke or use any Public, Friend, or Protected 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.

MyBase only refers to the immediate parent of the current class. If we create a SubSubClass that inherits from SubClass, the MyBase keyword would refer to the SubClass code, not the Parent code. There is no direct way to navigate more than one level up the inheritance chain.

MyClass Keyword

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:

Public Class Parent
  Public Sub DoSomething()
    OtherStuff()
  End Sub
  Public Overridable Sub OtherStuff()
    MessageBox.Show(“Parent other stuff”)
  End Sub
End Class

In this case, the DoSomething method calls the OtherStuff method to do some work. Notice however, that OtherStuff is marked as Overridable, so a subclass might provide a different implementation for the method. For example:

Public Class SubClass
  Inherits Parent
  Public Overrides Sub OtherStuff()
    MessageBox.Show(“SubClass other stuff”)
  End Sub
End Class

As we discussed earlier, VB.NET methods are virtual – which means that an object of type SubClass will always invoke OtherStuff from SubClass rather than from the Parent class. This is true even for code in the Parent class itself – so when the DoSomething method calls the OtherStuff method it will invoke the overridden implementation in SubClass.

This can be illustrated by the following client code:

Dim obj As New SubClass()
obj.DoSomething()

We will see a dialog displaying SubClass other stuff. The DoSomething method in the Parent class invokes the overridden OtherStuff implementation within the SubClass class.

If we don’t want this behavior – if we want the code in the Parent class to know for certain that it is calling the OtherStuff implementing from Parent – we need to use the MyClass keyword:

Public Class Parent
  Public Sub DoSomething()
    MyClass.OtherStuff()
  End Sub
  Public Overridable Sub OtherStuff()
    MessageBox.Show(“Parent other stuff”)
  End Sub
End Class

We can’t use the Me keyword, because that will reference the virtual method. MyClass, on the other hand, forces the call to be handled by the code in the same class as the call – in this case the Parent class.

This example is illustrated by the following diagram, which shows the method calls being made:

Here we can see that the client calls DoSomething, which is actually invoked from the Parent class. The Parent class then calls OtherStuff, but since it is implemented in SubClass, that is the implementation that is invoked.

Overriding the Constructor Method

We’ve already seen how we can override methods, and how to use the Me, MyBase, and MyClass keywords to interact with the various overridden methods in our inheritance chain. However, there are special rules that govern the process of overriding the New constructor method.

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:

Public Class SubClass
  Inherits Parent
  Public Sub New()
    MyBase.New()
    ‘ other initialization code goes here
  End Sub
End Class

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.

If all constructor methods of the base class require parameters then we must implement at least one constructor in our subclass and we must explicitly call MyBase.New from within our constructors.

As we discussed earlier, the New method can be overloaded, providing various implementations. If our parent class provides alternate implementations of New, 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.

MustInherit Keyword

Returning to our original sales order line item example, it may make little sense for anyone to create an object based on the generic LineItem class. In fact, we may want to ensure that only more specific subclasses derived from LineItem can be created. What we want to create is something called a base class. This is done using the MustInherit keyword in the class declaration:

Public MustInherit Class LineItem

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 LineItem class, so the following would cause a syntax error:

Dim obj As New LineItem()

Instead, to use the code in the LineItem class, we must create subclasses and use those throughout
our application.

MustOverride Keyword

Another option 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 MustOverride keyword on a method declaration:

Public MustOverride Sub CalcPrice()

Notice that there is no End Sub or any other code associated with the method.

When using MustOverride, 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

We can combine these two concepts – using both MustInherit and MustOverride – 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:

Public MustInherit Class Parent
  Public MustOverride Sub DoSomething()
  Public MustOverride Sub DoOtherStuff()
End Class

This technique can be very useful when creating frameworks or the high-level conceptual elements of a system. Any class that inherits Parent must implement both DoSomething and DoOtherStuff or a syntax error will result.

In some ways an abstract base class is very comparable to defining an interface using the Interface keyword. We’ll discuss the Interface keyword in detail later in this chapter. For now, be aware that the Interface keyword is used to formally declare an interface that can be implemented using the Implements keyword as in VB6.

We could define the same interface as shown in this example with the following code:

Public Interface IParent
  Sub DoSomething()
  Sub DoOtherStuff()
End Interface

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 Parent class, that class can in turn be subclassed. Those classes will automatically have DoSomething and DoOtherStuff methods due to the nature of inheritance.

Compare this with the interface approach, where each individual class must independently implement the IParent 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.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“XML is like violence - if it's not working for you, you're not using enough of it.”