Chapter 12. Inheritance

Inheritance is the feature that enables you to design classes that derive from simpler classes, known as base classes. Derived classes implement members defined within the base class and have the possibility of defining new members or of redefining inherited members. Members that a derived class inherits from the base one can be methods, properties, and fields, but such members must have Public, Protected, Friend, or Protected Friend scope. (See Chapter 7, “Class Fundamentals,” for details about scopes.) In .NET development, inheritance represents a typical “is-a” relationship. For a better understanding, let’s consider real life. When you say “person,” you identify a general individual. Every one of us is a person, with a first name and a last name. But a person also has a gender, either man or woman. In such a situation, a single person is the base class and a woman is a derived class because it inherits the name and last name attributes from the person but also offers a gender attribute. But this is only the first layer. Each man and each woman can have a job, and jobs are made of roles. So a woman can be employed by a company; therefore, as an employee she will have an identification number, a phone number, and an office room number. In this representation there is a deeper inheritance; because a person can be compared to a base class, a woman can be compared to an intermediate base class (also deriving from the person), and the employee is the highest level in the inheritance hierarchy. If we want to go on, we could still define other roles, such as lawyer, program manager, law clerk, pharmacist, and so on. Each of these roles could be represented by a class that derives from the employee role. As you can see, this articulate representation is something that in a development environment such as the .NET Framework enables defining a complex but powerful framework of objects. In this chapter you get a complete overview of the inheritance features in .NET Framework with Visual Basic 2012, and you will learn how you can take advantage of inheritance for both building hierarchical frameworks of custom objects and more easily reusing your code.

Applying Inheritance

Before explaining how inheritance is applied in code, a graphical representation can be useful. Figure 12.1 shows how you can create robust hierarchies of custom objects with inheritance.

Image

Figure 12.1. A graphical representation of a custom framework of objects using inheritance.

You derive a class from a base class using the Inherits keyword. For example, consider the following implementation of the Person class that exposes some basic properties:

Public Class Person

    Public Property FirstName As String
    Public Property LastName As String

    'A simplified implementation
    Public Function FullName() As String
        If FirstName = "" And LastName = "" Then
          Throw New _
          InvalidOperationException("Both FirstName and LastName are empty")
        Else
            Return String.Concat(FirstName, " ", LastName)
        End If
    End Function
End Class


Inherits System.Object

In the .NET Framework development, every class inherits from System.Object. Due to this, there is no need to add an inherits directive each time you implement a custom type because the Visual Basic compiler will do this for you behind the scenes.


The class also offers a FullName method that returns the concatenation of the two FirstName and LastName properties, providing a simplified and basic validation that is here for demonstration purposes. Now we can design a new class that inherits from Person, and in this scenario Person is the base class. The new class is named Contact and represents a personal contact in our everyday life:

Public Class Contact
    Inherits Person

    Public Property Email As String
    Public Property Phone As String
    Public Property BirthDate As Date
    Public Property Address As String

End Class

Contact is the derived class. It receives all public members from Person (in this example both the FirstName and LastName properties and the FullName method) and provides implementation of custom members. This is a typical application of .NET inheritance, in which one derived class inherits from the base class. The .NET Framework does not enable inheriting from multiple classes. You can create a derived class from only a base class. But in some situations multiple levels of inheritance would be required. Continuing the example of the Person class, you will meet several kinds of people in your life, such as customers, employees of your company, and personal contacts. All these people will have common properties, such as the first name and the last name; therefore, the Person class can be the base class for each of them, providing a common infrastructure that can then be inherited and customized. But if you consider a customer and an employee, both people will have other common properties, such as a title, a business phone number, and an email address. They will differ in the end because of the proper characteristics of their roles. For this purpose, you can implement intermediate classes that are derived classes from a first base class and base classes for other and more specific ones. For example, you could implement an intermediate infrastructure for both customers and employees. The following code snippet provides a class named BusinessPerson that inherits from Person:

Public Class BusinessPerson
    Inherits Person

    Public Property Email As String
    Public Property Title As String
    Public Property BusinessPhone As String

End Class

This class inherits the FirstName and LastName properties from Person (other than methods such as ToString and other public methods exposed by System.Object) and exposes other common properties for classes with a different scope. For example, both a customer and an employee would need the preceding properties, but each of them needs its own properties. Because of this, the BusinessPerson class is the intermediate derived class in the hierarchic framework of inheritance. Now consider the following classes, Customer and Employee:

Public Class Customer
    Inherits BusinessPerson

    Public Property CustomerID As Integer
    Public Property CompanyName As String
    Public Property Address As String
    Public Property ContactPerson As String

End Class

Public Class Employee
    Inherits BusinessPerson

    Public Property EmployeeID As Integer
    Public Property HomePhone As String
    Public Property MobilePhone As String
    Public Property HireDate As Date

End Class

Both classes receive the public properties from BusinessPerson, and both implement their custom properties according to the particular person they intend to represent. We can summarize the situation as follows:

Customer exposes the following properties:

FirstName and LastName, provided at a higher level by Person

Email, Title, and BusinessPhone provided by BusinessPerson

CustomerID, CompanyName, Address, and ContactPerson provided by its implementation

Employee exposes the following properties:

FirstName and LastName, provided at a higher level by Person

Email, Title, and BusinessPhone provided by BusinessPerson

EmployeeID, HomePhone, MobilePhone, and HireDate provided by its implementation

At a higher level, we also exposed a method named FullName, which has public visibility, so this method is also visible from derived classes.


Members’ Scope and Inheritance

Remember that only the Public, Protected, Friend, and Protected Friend members can be inherited within derived classes.


When available, you can use derived classes the same way as you would do with any other class, even if you do not know at all that a class derives from another one. The following, simple code demonstrates this:

'Employee inherits from BusinessPerson
'which inherits from Person
Dim emp As New Employee With {.EmployeeID = 1,
    .Title = "Dr.",
    .LastName = "Del Sole",
    .FirstName = "Alessandro",
    .Email = "[email protected]",
    .BusinessPhone = "000-000-000000",
    .HomePhone = "000-000-000000",
    .MobilePhone = "000-000-000000",
    .HireDate = New Date(5 / 30 / 2012)}

Until now, you’ve seen only properties in an inheritance demonstration. Methods are also influenced by inheritance and by interesting features that make them powerful.


Inheritance and Common Language Specification

The Common Language Specification (CLS) establishes that a CLS-compliant class must inherit only from another CLS-compliant class; otherwise, it will not be CLS-compliant.


Illustrating System.Object in Detail

As you should remember from Chapter 4, “Data Types and Expressions,” in.NET development all types implicitly derive from System.Object, considering both reference and value types. Because of the inheritance relationship, custom types also inherit some methods, so you have to know them. Table 12.1 summarizes inherited methods.

Table 12.1. System.Object Methods

Image

You need to understand which members are exposed by System.Object because they will all be inherited by your custom classes and by all built-in classes in the .NET Framework. I already discussed GetType and Finalize methods in Chapter 4 and Chapter 8, respectively. Such methods are inherited by all .NET types. The GetHashCode method returns the hash that is assigned at runtime by the CLR to a class instance. The following code provides an example:

Dim p As New Object

Dim hashCode As Integer = p.GetHashCode
Console.WriteLine(hashCode.ToString)

On my machine the code produces the following result: 33156464. This is useful to uniquely identify a class’s instance. New is the constructor, as described in Chapter 7. When creating custom classes, a constructor is inherited and implicitly defined within classes and constitutes the default constructor. Object also exposes two shared members—Equals and ReferenceEquals—which return a Boolean value. It’s worth mentioning that shared methods are also inherited by derived classes, but they cannot be overridden (as better described in next section). For example, the following code establishes whether both specified objects are considered the same instance:

'Two different instances
Dim firstObject As New Object
Dim secondObject As New Object

'Returns False
Dim test As Boolean = Object.ReferenceEquals(firstObject, secondObject)

Next, the code instead checks whether two instances are considered equal by the compiler:

'Returns False
Dim test As Boolean = Object.Equals(firstObject, secondObject)

There is also an overload of the Equals method that is instead an instance method. The following code shows an example of instance comparisons using Equals:

'Returns False
Console.WriteLine(firstObject.Equals(secondObject))
'Copies the reference to the instance
Dim testObject As Object = firstObject
'Returns True
Console.WriteLine(testObject.Equals(firstObject))

For assignments, you can always assign any type to an Object instance, as demonstrated here:

Dim aPerson As New Person
Dim anObject As Object = aPerson

Because Object is the mother of all classes, it can receive any assignment. The last method in System.Object (that you will often use) is ToString. This method provides a string representation of the object. Because System.Object is the root in the class hierarchy, this method just returns the pure name of the class. Therefore, the following line of code returns System.Object:

Console.WriteLine(firstObject.ToString)

But this is not appropriate for value types, in which you need a string representation of a number, or for custom classes, in which you need a custom representation. Taking the example of the famous Person class, it would be more useful to get a string composed by the last name and the first name instead of the name of the class. Fortunately, the .NET Framework inheritance mechanism provides the capability to change the behavior of inherited members as it is exposed by base classes; this is known as overriding.

Introducing Polymorphism

Polymorphism is another key concept in object-oriented programming (OOP). As its name implies, polymorphism enables an object to assume different forms. In .NET development, it means you can treat an object as another one, due to the implementation of common members. A first form of polymorphism is when you assign base classes with derived classes. For example, both Contact and Customer classes are derived of the Person class. Now consider the following code:

Dim c As New Contact
Dim cs As New Customer

'C is of type Contact
Dim p As Person = c

The new instance of the Person class receives an assignment from an instance of the Contact class. This is always possible because Person is the parent of Contact (in which base is the parent of derived). Therefore, you might also have the following assignment:

'Cs is of type Customer
Dim p As Person = cs

In this scenario, Person is polymorphic in that it can “impersonate” multiple classes that derive from itself.


Retrieving the Actual Type

Use the TypeOf operator, discussed in Chapter 4, to check whether the polymorphic base class is representing a derived one.


Polymorphism is useful when you need to work with different kinds of objects using one common infrastructure that works the same way with all of them. Now let’s continue with the preceding example. The Person class exposes the usual FirstName and LastName properties also common to Contact and Customer. At this point, you can remember how our previous implementations of the Person class offered a method named FullName that returns the concatenation of both the LastName and FirstName properties. For the current discussion, consider the following simplified version of the FullName method, as part of the Person class:

Public Function FullName() As String
    Return String.Concat(FirstName, " ", LastName)
End Function

All classes deriving from Person inherit this method. All deriving classes do need a method of this kind for representing the full name of a person, but they would need different implementations. For example, the full name for a customer should include the company name, whereas the full name for a personal contact should include the title. This means that all classes deriving from Person will still need the FullName method (which is part of the commonalities mentioned at the beginning of this section) but with a custom implementation fitting the particular need. For this, the .NET Framework enables realizing polymorphism by overriding members, as the next section describes.


Note on Polymorphism

Overriding is the most important part of polymorphism in .NET development, but interfaces also play a role. Chapter 13, “Interfaces,” explains how interfaces complete polymorphism.


Overriding Members

When a class derives from another one, it inherits members and the members behave as they are defined in the base class. (For this purpose, remember the scope.) As for other .NET languages, Visual Basic enables redefining inherited methods and properties so that you can change their behavior. This technique is known as overriding and requires a little work on both the base class and the derived class. If you want to provide the ability of overriding a member, in the base class you have to mark the member as Overridable. Let’s continue the example of the Person class, defined as follows:

Public Class Person

    Public Property FirstName As String
    Public Property LastName As String

    'Simplified version, with no validation
    Public Function FullName() As String
        Return String.Concat(LastName, " ",
                             FirstName)
    End Function

End Class

The goal is providing derived classes the capability of overriding the FullName method so that they can provide a custom and more appropriate version. The method definition must be rewritten as follows:

Public Overridable Function FullName() As String

At this point, we could provide a simplified version of the Contact class, inheriting from Person. Such implementation will override the FullName method to provide a custom result. Let’s begin with the following code:

Public Class Contact
    Inherits Person

    Public Property Email As String

    Public Overrides Function FullName() As String
        'By default returns the base class'
        'implementation
        Return MyBase.FullName()
    End Function
End Class

Two things are important here. First, the Overrides keyword enables you to redefine the behavior of a member that has been marked as Overridable in the base class. Second, Visual Studio automatically provides an implementation that is the behavior established in the base class, due to the MyBase keyword, which is discussed later. IntelliSense is powerful in this situation, too, because when you type the Overrides keyword, it shows all overridable members, making it easier to choose what you have to override (see Figure 12.2).

Image

Figure 12.2. IntelliSense helps you choose overridable members.

Figure 12.2 can also help you understand which members from System.Object are overridable. The instance overload of Equals, GetHashCode, and ToString are methods you can redefine. You cannot instead override (neither mark as Overridable) shared members, and this is demonstrated by the fact that the shared overload of Equals and ReferenceEquals are not available in the IntelliSense pop-up window. At this point, we could provide a new implementation of the FullName method specific for the Contact class:

Public Overrides Function FullName() As String
    'A simplified implementation
    'with no validation
    Dim result As New Text.StringBuilder
    result.Append(Me.FirstName)
    result.Append(" ")
    result.Append(Me.LastName)
    result.Append(", Email:")
    result.Append(Me.Email)

    Return result.ToString
End Function

Now you can create a new instance of the Contact class and invoke the FullName method to understand how overriding changed its behavior:

Dim testContact As New Contact With _
    {.FirstName = "Alessandro",
     .LastName = "Del Sole",
     .Email = "[email protected]"}
Console.WriteLine(testContact.FullName)

The preceding code produces the following result:

Alessandro Del Sole, Email:[email protected]

Such a result is, of course, more meaningful if related to the specific kind of class. Another common situation is redefining the behavior of the ToString method that is inherited from Object and that is marked as Overridable. For example, in the Contact class we could override ToString as follows:

Public Overrides Function ToString() As String
    Dim result As New Text.StringBuilder
    result.Append(Me.FirstName)
    result.Append(" ")
    result.Append(Me.LastName)
    result.Append(", Email:")
    result.Append(Me.Email)

    Return result.ToString

End Function

We can then invoke this method as follows:

Console.WriteLine(testContact.ToString)

Typically, overriding ToString is more appropriate if you need to return a string representation of a class, as in the preceding example. The FullName method is just an example of how you can override a custom method that is defined in a base class and that is not inherited from System.Object.


Overridden is Overridable

When a member is overridden using the Overrides keyword, the member is also implicitly Overridable. Because of this, you cannot use the Overridable keyword on a member marked with Overrides; the compiler would throw an error message, requiring you to remove the Overridable keyword.


NotOverridable Keyword

You can mark an overridden method or property as NotOverridable so that derived classes cannot override them again. The NotOverridable keyword cannot be used versus methods or properties that do not override a base member. Continuing the example of the Contact class previously defined, the NotOverridable keyword can be used as follows:

Public NotOverridable Overrides Function FullName() As String
    Return String.Concat(MyBase.FullName(), ": ", Email)
End Function

In this way the FullName method within the Contact class overrides the base class, but derived classes cannot override it again. NotOverridable is used only within derived classes that override the base class’s members because in a base class the default behavior for members is that they cannot be overridden unless you explicitly mark them as Overridable.

Overloading Derived Members

You can use the overloading technique described in Chapter 7 within derived classes, with a few differences. You saw in Chapter 7 how overloaded members must not be marked with the Overloads keyword within a class. Instead, a derived class using the Overloads keyword is mandatory if you implement a new overload of a member with a different signature. The following code provides an example of overloading the FullName method within the Contact class you previously saw:

Public Overloads Function FullName(ByVal Age As Integer)
    Return MyBase.FullName & " of age: " & Age.ToString
End Function

If another signature of the member is available within the derived class, the Overloads keyword is required; otherwise, the compiler throws a warning message saying that another signature is declared as Overrides or Overloads. If, instead, no other signatures are available within the derived class, the Overloads keyword is required to prevent from shadowing the base class’s member. Shadowing is discussed at the end of this chapter, in the section “Shadowing.”

Conditioning Inheritance

Inheritance is an important feature in OOP with .NET. You might have situations in which inheritance is not a good option—for example, when you want to prevent others from accessing members in the base class. Or there could be custom frameworks implementations in which a high-level class should not be used directly, and therefore it should be always inherited. The Visual Basic language enables accomplishing both scenarios via special keywords, as discussed in the next section.

NotInheritable Keyword

There are situations in which you might want to prevent inheritance from your classes. This can be useful if you do not want a client to modify in any way the base object’s behavior and its members. To accomplish this, you simply need to mark a class with the NotInheritable keyword. The following code shows an example of a class that cannot be derived:

Public NotInheritable Class BusinessPerson
    Inherits Person

    Public Property Email As String
    Public Property Title As String
    Public Property BusinessPhone As String
End Class

As you can see, the BusinessPerson class is marked as NotInheritable and cannot be derived by other classes. It can still inherit from other classes but, obviously, members cannot be marked as Overridable because they are not inheritable. Another typical example of classes that cannot be inheritable is when you have a class exposing only shared members, as shown in the following code:

<CLSCompliant(True)>
Public NotInheritable Class CompressionHelper

    Private Sub New()

    End Sub

    Public Shared Sub CompressFile(ByVal source As String,
                                   ByVal target As String)
        'Your code goes here
    End Sub

    Public Shared Sub DecompressFile(ByVal compressed As String,
                                     ByVal original As String)
        'Your code goes here
    End Sub
End Class

The class is also decorated with the CLSCompliant attribute because such a situation is explicitly established by the Common Language Specification. NotInheritable is the Visual Basic counterpart of the sealed keyword in Visual C#. It’s important to know the C# representation because in .NET terminology not-inheritable classes are defined as sealed and many analysis tools use this last word. NotInheritable classes provide better performance; the compiler can optimize the usage of this type of classes, but you cannot blindly use classes that cannot be inherited only to avoid a small overhead. You should always design classes that fit your needs.


Note

As a better programming practice, developers should always mark classes with NotInheritable unless they explicitly plan for the class to be inheritable by a consuming class.


MustInherit and MustOverride Keywords

Inheritance is powerful because it enables building custom objects’ frameworks. In this context, an object can represent the base infrastructure for different kinds of classes. We saw how the Person class is the base infrastructure for the Customer, Employee, and Contact derived classes. Because of its implementation, the Person class does nothing special. It has a generic behavior, and you will probably never create instances of that class; it is more likely that you will create instances of its derived classes. In this scenario, therefore, when you have a general-purpose base class that acts just as a basic infrastructure for derived classes, you can force a class to be inherited so it cannot be used directly. To accomplish this, Visual Basic provides the MustInherit keyword that states that a class will work only as a base class and cannot be used directly unless you create a derived class.


Abstract Classes

In .NET terminology, classes marked as MustInherit are also known as abstract classes. This is important to remember because you will often encounter this term within the documentation and in several analysis tools.


The following code shows a new implementation of the Person class:

Public MustInherit Class Person

    Public Property FirstName As String
    Public Property LastName As String
End Class

Now you can derive classes only from Person. Another interesting feature is the capability to force members to be overridden. This can be accomplished using the MustOverride keyword on methods and properties. Continuing with the example of the Person class, we can rewrite the FullName method definition as follows:

Public MustOverride Function FullName() As String

When you mark a method with MustOverride, the method has no body. This makes sense because, if it must be redefined within a derived class, it would not be very helpful providing a base implementation. The same thing happens with properties, meaning that you will have only a declaration.

Inheriting from an Abstract Class

When you create a class that inherits from an abstract class (that is, marked as MustInherit), the only thing you need to pay particular attention to is overriding members. To help developers in such a scenario, the Visual Studio IDE automatically generates members’ stubs for methods and properties marked as MustOverride in the base abstract class. So, if you create a new implementation of the Contact class, when you press Enter after typing the Inherits line of code, Visual Studio generates an empty stub for the FullName method as follows:

Public Class Contacts
    Inherits Person

    Public Overrides Function FullName() As String

    End Function
End Class

Now you can be sure that all MustOverride members have an implementation. In our example you might want to complete the code by adding the implementation shown in the “Overriding Members” section in this chapter.

Abstract Classes and Common Language Specification

The Common Language Specification contains a small rule regarding abstract classes. This rule establishes that to be CLS-compliant, members in abstract classes must explicitly be marked as CLSCompliant. The following code provides an example:

<CLSCompliant(True)>
Public MustInherit Class Person

    <CLSCompliant(True)> Public Property FirstName As String
    <CLSCompliant(True)> Public Property LastName As String

    <CLSCompliant(True)> Public MustOverride Function FullName() As String

End Class

Accessing Base Classes Members

Sometimes you need to access the base classes’ members from derived classes. There are several reasons for doing this, so you need to know how. Visual Basic provides two special keywords for invoking base members, MyBase and MyClass. Both are discussed in this section.

MyBase Keyword

When you need to get a reference to the base class of the derived class you are working on, you can invoke the MyBase keyword. This keyword represents an instance of the base class and enables you to work on members as they are exposed by the base class, instead of the ones exposed by the derived class. Consider the following implementation of the Person class, in which a FullInformation method provides a representation of all the information supplied to the class:

Public Class Person

    Public Property FirstName As String
    Public Property LastName As String
    Public Property Age As Integer

    Public Overridable Function FullInformation() As String
        Dim info As New Text.StringBuilder

        info.Append("Name: ")
        info.Append(Me.FirstName)
        info.Append(" Last name: ")
        info.Append(Me.LastName)
        info.Append(" Age: ")
        info.Append(Me.Age.ToString)
        Return info.ToString
    End Function
End Class

Now we can create a new implementation of the Contact class, inheriting from Person. A new class needs to override the FullInformation method from the base class. When you type the Overrides keyword, Visual Studio generates a default implementation that looks like the following:

Public Overrides Function FullInformation() As String
    Return MyBase.FullInformation
End Function

The code returns the result offered by the FullInformation method as it is implemented in the base class, which is accomplished via the MyBase keyword. We can now rewrite the complete code for the Contact class as follows:

Public Class Contact
    Inherits Person

    Public Property Title As String

    Public Overrides Function FullInformation() As String
        Dim firstInfo As String = MyBase.FullInformation

        Dim newInfo As New Text.StringBuilder
        newInfo.Append(firstInfo)
        newInfo.Append(" Title: ")
        newInfo.Append(Me.Title)
        Return newInfo.ToString
    End Function
End Class

Notice that the overridden method does not perform a complete string concatenation while it invokes first the MyBase.FullInformation method. This is a best practice because one of inheritance’s purposes is favoring code reusability; therefore, this invocation is better than rewriting the code from scratch. The following code snippet shows how you can interact with both base class and derived class properties, assuming that FirstName and LastName have been declared as Overridable in the base class and overridden within the derived class:

Public Sub New(ByVal name As String,
               ByVal surName As String,
               ByVal age As Integer,
               ByVal title As String)

    'Goes to the base class properties
    MyBase.FirstName = name
    MyBase.LastName = surName

    'Current instance properties
    Me.Age = age
    Me.Title = title
End Sub


Me and MyBase

The Me keyword refers to the instance of the current class, whereas MyBase refers to the base class from which the current class derives. This difference is evident when a member is overridden, but if members are not redefined, both keywords refer to the same code.


The next section details a few things you need to know about constructors within derived classes.

MyClass Keyword

Another way of accessing the base classes’ members is the MyClass keyword. Imagine you have a base class exposing some overridable members, such as properties or methods; then you have a derived class that overrides those members. The MyClass keyword avoids the application of overriding and invokes members on the derived class as if they were NotOverridable on the base class. In other words, MyClass enables executing members of a base class in the context of a derived class, ensuring that the member version is the one in the base class. Listing 12.1 shows an example.

Listing 12.1. Demonstrating the MyClass Keyword


Public Class BaseClassDemo

    Public Overridable ReadOnly Property Test As String
        Get
            Return "This is a test in the base class"
        End Get
    End Property

    Public Function DoSomething() As String
        Return MyClass.Test
    End Function
End Class

Public Class DerivedClassDemo
    Inherits BaseClassDemo

    Public Overrides ReadOnly Property Test As String
        Get
            Return "This is a test in the derived class"
        End Get
    End Property
End Class
Module Module1
    Sub Main()

        Dim derived As New DerivedClassDemo

        'Invokes the member within the derived
        'class but as if it was not overridden
        Dim result As String = derived.DoSomething
    End Sub
End Module


The BaseClassDemo base class exposes an overridable property that returns a text message, for demo purposes. It also exposes a public method that just shows the text stored within the Test property. Within the derived DerivedClassDemo, the Test property is overridden but the DoSomething method is not. This method is still available when you create an instance of the DerivedClassDemo class. Because the method is defined within the base class and then is executed within the derived class’s context, if you implemented the method as follows:

Public Function DoSomething() As String
    Return Me.Test
End Function

it would return the content of the derived Test property. In some situations, though, you might want to ensure that only base class members are used within other members that are not overridden; this can be accomplished using the MyClass keyword. If you run the code shown in Listing 12.1, the result variable contains the string "This is a test in the base class", although the DoSomething method has been invoked on an instance of the derived class. You can still use the overridden Test property for other purposes in your derived class. MyClass is similar to Me in that both get a reference to the instance of the current class, but MyClass behaves as if members in the base class were marked as NotOverridable and therefore as if they were not overridden in the derived class.

Constructors’ Inheritance

The previous section discussed the MyBase keyword and how it can be used to access members from a base class. The keyword also has another important purpose when it comes to constructors. Consider the following constructor, which is implemented within the Person class (that is, the base class) shown in the previous section:

Public Sub New(ByVal firstName As String,
               ByVal lastName As String,
               ByVal age As Integer)

    Me.FirstName = firstName
    Me.LastName = lastName
    Me.Age = age
End Sub

The problem now is in derived classes. The rule is that if you have a constructor receiving arguments in the base class, you do need to provide a constructor receiving arguments also within a derived class, and the constructor needs to invoke the base class. The following code shows how a constructor needs to be implemented within the Contact class:

Public Sub New(ByVal name As String,
               ByVal surName As String,
               ByVal age As Integer,
               ByVal title As String)

    MyBase.New(name, surName, age)
    Me.Title = title
End Sub

As you can see from the preceding code snippet, the first line of code is an invocation to the constructor of the base class—and this is a rule that you must follow. After that line of code, you can provide any other initialization code. This particular requirement is necessary if you plan to provide a constructor that receives arguments within the base class, although it’s not necessary if you implement a constructor that does not receive arguments or if you do not provide any constructor (which is implicitly provided by the Visual Basic compiler).

Shadowing

The beginning of this chapter explained that classes can inherit from base classes exposed by class libraries such as .dll assemblies and that you do not necessarily need the source code. You could create a class deriving from another class exposed by a compiled assembly and implement a new member. In addition, the publisher of the compiled base class could release a new version of the class, providing a member with the same name of your custom member. In this case, you would not be able to edit the base class because you wouldn’t have the source code. Visual Basic 2012 provides an interesting way of dealing with such a situation known as shadowing. Although the Visual Basic compiler still enables compiling (it throws warning messages), your class needs to “shadow” the member with the same name of your custom one. This is accomplished using the Shadows keyword. Consider this particular implementation of the Person class, exposing a Title property, of type String:

Public Class Person

    Public Property FirstName As String
    Public Property LastName As String
    Public Property Title As String

End Class

Now consider the following implementation of the Contact class, which requires a value defined within the Titles enumeration:

Public Class Contact
    Inherits Person

    Public Property Title As Titles
End Class

Public Enum Titles
    Dr
    Mr
    Mrs
End Enum

The Contact class exposes a Title property, but its base class already has a Title property; therefore, the Visual Basic compiler shows a warning message related to this situation. If you want your code to use the derived Title property, you need to mark your member with Shadows as follows:

Public Class Contact
    Inherits Person

    Public Shadows Property Title As Titles
End Class

You can accomplish the same result by marking the property within the derived class as Overloads:

Public Overloads Property Title As Titles

In this particular example we can use auto-implemented properties. If the property within the derived class returned the same type of the one within the base class, it would make more sense using old-fashioned properties so that you have the ability to customize the behavior.


Be Very Careful when Shadowing

If you have a derived class that shadows some property or method (like Contact in the example above), and you pass an instance to a method that accepts an argument of the base class type (like Person), if the method makes calls to something that was shadowed, it will take the base implementation, even though you passed in the derived type. This can lead to unexpected results, so be very careful when shadowing.


Overriding Shared Members

Shared members cannot be overridden. This means that you can only use them as they have been inherited from the base class or provide a shadowing implementation for creating a new definition from scratch. For example, consider this simplified implementation of the Person class, which exposes a shared Counter property:

Public Class Person

    Public Shared Property Counter As Integer
End Class

If you now create a Contact class that inherits from Person, you can use the Counter property as previously implemented, or you can shadow the base definition as follows:

Public Class Contact
    Inherits Person

    Public Shared Shadows Property Counter As Integer
End Class

If you intend to provide an overloaded member with a different signature, you can use overloading as follows:

Public Shared Shadows Property Counter As Integer
Public Shared Shadows Property Counter(ByVal maximum As Integer) As Integer
   Get

   End Get
   Set(ByVal value As Integer)

   End Set
End Property

Another limitation of shared members is that you cannot invoke the MyBase and MyClass keywords within them. Moreover, you cannot invoke shared members using the MyBase keyword. So, if you were to assign the Counter shared property defined in the person class, you would have to write Person.Counter = 0 instead of MyBase.Counter = 0.

Practical Inheritance: Building Custom Exceptions

In Chapter 6, “Handling Errors and Exceptions,” you learned about exceptions in .NET development; you saw what exceptions are and how you can intercept them at runtime to create well-formed applications that can handle errors. The .NET Framework ships with hundreds of exceptions related to many aspects of .NET development. You might encounter a situation where you need to implement custom exceptions. You can build custom exceptions due to inheritance. A custom exception can inherit from the root System.Exception class or from another exception (such as System.IO.IOException) that necessarily inherits from System.Exception. Custom exceptions should always be CLS-compliant. Let’s look at the Person class implementation again, adding a method that returns the full name of the person and that requires at least the last name:

Public Class Person

    Public Property FirstName As String
    Public Property LastName As String

    Public Function FullName() As String

        If String.IsNullOrEmpty(Me.LastName) Then
            Throw New MissingLastNameException("Last name not specified")
        Else
            Return String.Concat(LastName, " ", FirstName)
        End If
    End Function
End Class

As you can see, if the LastName property contains an empty or null string, the code throws a MissingLastNameException. This exception is custom and must be implemented.


Naming Conventions

Remember that every exception class’s identifier must terminate with the Exception word. Microsoft naming convention rules require this.


The MissingLastNameException is implemented as follows:

<Serializable()>
Public Class MissingLastNameException
    Inherits Exception

    Public Sub New()
        MyBase.New()
    End Sub

    Public Sub New(ByVal message As String)
        MyBase.New(message)
    End Sub

    Public Sub New(ByVal message As String, ByVal inner As Exception)
        MyBase.New(message, inner)
    End Sub

    Protected Sub New(ByVal info As Runtime.Serialization.SerializationInfo,
                      ByVal context As _
                      Runtime.Serialization.StreamingContext)
        MyBase.New(info, context)
    End Sub
End Class

There is a series of considerations:

• As a custom exception, it inherits from System.Exception.

• The class is decorated with the Serializable attribute; this is one of the CLS establishments, and it enables developers to persist the state of the exception to disk (see Chapter 41, “Serialization”).

• Custom exceptions expose three overloads of the base constructors plus one overload marked as Protected that is therefore available to eventually derive classes and that receives information on serialization.

• Custom exceptions are caught exactly as any other built-in exception.

When you have your custom exception, you can treat it as other ones:

Try
    Dim p As New Person
    'Will cause an error because
    'the LastName was not specified
    Console.WriteLine(p.FullName)
Catch ex As MissingLastNameException
    Console.WriteLine("ERROR: please specify at least the last name")
Catch ex As Exception
    Console.WriteLine("Generic error")
    Console.WriteLine(ex.ToString)
Finally
    Console.ReadLine()
End Try

The preceding code intentionally causes an exception to demonstrate how the MissingLastNameException works, by not assigning the LastName property in the Person class instance.


Avoid Complex Inheritance Chains

Building a complex inheritance chain is something that should be carefully considered because as the chain grows, the child classes will be tied and rely on the base classes not changing, at risk of disrupting the inheritance chain.


Summary

Inheritance is a key topic in the object-oriented programming with Visual Basic and the .NET Framework. In this chapter, you learned lots of important concepts. First, you learned what inheritance is and how you can take advantage of it for creating frameworks of custom objects. Then you saw how to derive classes from base classes using the Inherits keyword and how your derived classes automatically inherit some members from System.Object, which is the root in the class hierarchy. When deriving classes, you need to consider how constructors and shared members behave; the chapter also provided an overview. But inheritance would be of less use without polymorphism. You learned how polymorphism requires implementing one common infrastructure for multiple objects, taking advantage of the capability to redefine inherited members’ behavior. For this purpose, the .NET Framework enables the overriding technique that you can use to modify the behavior of derived members. Additionally, with shadowing you can override a member from a class of which you do not have the source code. You often want to condition inheritance, establishing when classes should be sealed (NotInheritable) or abstract (MustInherit). In inheritance scenarios, you also often need to have access to base class members; therefore, you need the MyBase and MyClass keywords. But you seldom work with theories. Because of this, the chapter closed with a practical demonstration of inheritance, showing how you can build custom exceptions to use in your applications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.46.69