More about Inheritance

043 - COM and the Fragile Base Class Problem <p><script> function OpenWin(url, w, h) { if(!w) w = 400; if(!h) h = 300; window. open (url, "_new", "width=" + w + ",height=" + h + ",menubar=no,toobar=no,scrollbars=yes", true); } function Print() { window.focus(); if(window.print) { window.print(); window.setTimeout('window.close();',5000); } } </script></p>
Team-Fly    

 
Application Development Using Visual Basic and .NET
By Robert J. Oberg, Peter Thorsteinson, Dana L. Wyatt
Table of Contents
Chapter 5.  Inheritance and Exceptions in VB.NET


Our case study has illustrated many important features of object-oriented programming, but there is more to the story. Methods in a derived class may hide the corresponding method in the base class, possibly making use of the base class method in their implementation. Alternatively, the base class may have virtual methods , which are not bound to an object at compile time but are bound dynamically at runtime. A derived class may override a virtual method. This dynamic binding behavior enables polymorphic code, which is general code that applies to classes in a hierarchy, and the specific class that determines the behavior is determined at runtime.

VB.NET provides keywords Overridable and Overrides that precisely specify in base and derived classes, respectively, that the programmer is prescribing dynamic binding. By providing a mechanism to specify polymorphic behavior in the language, VB.NET helps programs deal with an issue known as the fragile base class problem , which can result in unexpected behavior in a program when a base class in a library is modified but the program that uses the library is unchanged.

Employee Class Hierarchy

In this section we will use a much simpler class hierarchy to illustrate some important concepts. The base class is Employee , which has a public field Name . There are two derived classes. The SalaryEmployee class has a private m_salary field. The WageEmployee class has private fields for an hourly m_rate of pay and for the number of m_hours worked. Figure 5-3 illustrates this simple class hierarchy.

Figure 5-3. Employee class hierarchy.

graphics/05fig03.gif

Method Hiding

A derived class inherits the methods of its base class, and these inherited methods are automatically available "as is." Sometimes we may want the derived class to do something a little different for some of the methods of the base class. In this case we will put code for these changed methods in the derived class, and we say the derived class "hides" the corresponding methods in the base class. Note that hiding a method requires that the signatures match exactly, and the Shadows keyword is used. (As we discussed in Chapter 4, methods have the same signature if they have the same number of parameters, and these parameters have the same types and modifiers, such as ByRef or ByVal . The return type does not contribute to defining the signature of a method.)

In VB.NET, if you declare a method in a derived class that has the same signature as a method in the base class, you will get a compiler warning message. In such a circumstance, there are two things you may wish to do. The first is to hide the base class method, which is what we discuss in this section. The second is to override the base class method, which we will discuss in the next section.

To hide a base class method, place the keyword Shadows in front of the method in the derived class. When you hide a method of the base class, you may want to call the base class method within your implementation of the new method. You can do this by using the keyword MyBase , followed by a period, followed by the method name and parameters.

The example program HideEmployee illustrates method hiding. This program has the Employee base class and the SalaryEmployee derived class. Each class has a Show method. The derived class's Show method hides the Show method of the base class. But the derived class can call the base class Show method through the MyBase keyword. Here is the code:

 graphics/codeexample.gif ' Employee.vb Imports System  Public Class Employee  Public Name As String    Public Sub New(ByRef name As String)       Me.Name = name    End Sub  Public Sub Show()  Console.WriteLine("name = {0}", Name)    End Sub End Class  Public Class SalaryEmployee   Inherits Employee  Private m_salary As Decimal    Public Sub New(_     ByVal name As String, ByVal salary As Decimal)       MyBase.New(name)       Me.m_salary = salary    End Sub  Public Shadows Sub Show()   MyBase.Show()  Console.WriteLine("salary = {0:C}", m_salary)    End Sub End Class 

If you delete the Shadows keyword in the derived class Show method, you will get a compiler warning message:

 warning BC40004: 'sub Show' conflicts with 'sub Show' on the base class 'Employee' - they have the same name.  Use the 'Shadows' keyword if you want to hide the name in the base class. 

Static Binding

In VB.NET the normal way methods are tied to classes is through static binding . That means that the object reference type is used at compile time to determine the class whose method is called. The HideEmployee program we just looked at illustrates static binding, using a simple Employee class and a derived SalaryEmployee class. Here is the test program:

 ' TestEmployee.vb Imports System Module TestEmployee    Sub Main()       Dim emp As Employee =  New Employee  ("Ellen")       Dim sal As SalaryEmployee = _  New SalaryEmployee  ("Sally", 100D)       emp.Show()       sal.Show()       'sal = emp       emp = sal       emp.Show()    End Sub End Module 

In this program emp is an object reference of type Employee . Calling Show through this object reference will always result in Employee.Show being called, no matter what kind of object emp may actually be referring to. Here is the output. Notice that the second time we call Show through emp we are still getting the Employee version of Show (only the name is displayed).

 name = Ellen name = Sally salary = 0.00  name = Sally  Press any key to continue 
Type Conversions in Inheritance

This program also illustrates another feature of inheritance, type conversions. After the objects emp and sal have been instantiated , the object references will be referring to different types of objects, one of type Employee and the other of type SalaryEmployee . Note that the SalaryEmployee object has an additional field, m_salary .

The test program tries two type conversions:

 'sal = emp emp = sal 

The first assignment is illegal, as you can verify by uncommenting it, recompiling, and then trying to run the program. This will result in a System.InvalidCastException being thrown. Suppose the assignment were allowed. Then you would have an object reference of type SalaryEmployee referring to an Employee object. If the conversion "down the hierarchy" (from a base class to a derived class) were allowed, it would result in data corruption if the nonexistent member were written to.

 emp = sal 

This assignment is perfectly safe and legal. We are converting "up the hierarchy." This is okay because of the IS-A relationship of inheritance. A salary employee "is an" employee. It is a special kind of employee. Everything that applies to an employee also applies to a salary employee. There is no "extra field" in the Employee class that is not also present in the SalaryEmployee class.

Virtual Methods

In VB.NET you can specify that a method in VB.NET will be bound dynamically . Only at runtime will it be determined whether the base or derived class's method will be called. The program VirtualEmployee illustrates this behavior. The file VirtualEmployee.vb contains class definitions for a base class and a derived class, as before. But this time the Show method is declared as virtual in the base class with the Overridable keyword. In the derived class the Show method is declared the Overrides keyword (in place of Shadow that we used before with method hiding). Now the Show method in the derived class does not hide the base class method but rather overrides it.

 graphics/codeexample.gif ' VirtualEmployee.vb Imports System Public Class Employee    Public Name As String    Public Sub New(ByVal name As String)       Me.Name = name    End Sub  Public Overridable Sub Show()  Console.WriteLine("name = {0}", Name)    End Sub End Class Public Class SalaryEmployee    Inherits Employee    Private m_salary As Decimal    Public Sub New(_     ByVal name As String, _     ByVal salary As Decimal)       MyBase.New(name)       m_salary = salary    End Sub  Public Overrides Sub Show()  MyBase.Show()       Console.WriteLine("salary = {0:C}", m_salary)    End Sub End Class 

We use the same test program as in the previous example. Here is the output. Now, the second time we call Show through sal , we will be getting the SalaryEmployee.Show method, showing the salary as well as the name, rather than the Employee.Show method of the base class. This is known as virtual method invocation, or dynamic binding, and it is polymorphism in action.

 name = Ellen name = Sally salary = 0.00  name = Sally   salary = 0.00  Press any key to continue 
Virtual Methods and Efficiency

Virtual method invocation (dynamic binding) is slightly less efficient than calling an ordinary nonvirtual method (static binding). With a virtual method call, there is some overhead at runtime associated with determining which class's method will be invoked. VB.NET allows you to specify in a base class whether you want the flexibility of a virtual method or the slightly greater efficiency of a nonvirtual method. You simply decide whether or not to use the keyword Overridable . (In some languages all methods are virtual, and you don't have this choice.)

Method Overriding

The Overrides keyword in VB.NET is very useful for making programs clearer. In some languages, such as C++, there is no special notation for overriding a method in a derived class. You simply declare a method with the same signature, as a method in the base class, and overriding is implicit, or even accidental. If the base class method is a virtual method, the behavior is to override. If the base class method is not virtual, the behavior is to hide. In VB.NET this behavior is made explicitly clear in the derived class.

The Fragile Base Class Problem

One subtle pitfall in object-oriented programming is the fragile base class problem. Suppose the Overrides keyword syntax did not exist. Suppose further that you derive a class from a third-party class library, and you have a method in the derived class that does not hide or override any method in the base class.

Now a new version of the class library comes out, and the base class has a new virtual method whose signature happens to match one of the methods in your class. Now you can be in trouble! Classes that derive from your class may now behave in unexpected ways. Code that was "expected" to call the new method in the class libraryor in code in a derived class that deliberately overrides this methodmay now call your method that has nothing whatever to do with the method in the class library.

This situation is rare, but if it occurs, it can be extremely vicious. Fortunately, VB.NET helps you avoid such situations by requiring you to use the Overrides keyword if you are indeed going to perform an override. If you do not specify either Overrides or Shadow and a method in your derived class has the same signature as a method in a base class, you will get a compiler warning. Thus, if you build against a new version of the class library that introduces an accidental signature match with one of your methods, the compiler will warn you.

COM and the Fragile Base Class Problem

There is no implementation inheritance in Microsoft's Component Object Model (COM). Microsoft used the fragile base class problem as a rationale for not providing implementation inheritance in COM. The issue is much more important for binary components , such as COM objects, than for traditional class libraries distributed with source code, because if the problem arises and you have no source for the library, your options are limited. The real killer is for the problem not to reveal itself during development and testing, but to crop up in the field after the application has been deployed.

Microsoft .NET has aims similar to those COM had in providing binary components in multiple languages. The VB.NET Override concept uses a corresponding feature of .NET, so .NET is able to effectively utilize implementation inheritance with less risk than was the case with COM.

Polymorphism

Virtual functions make it easy to write polymorphic code in VB.NET. Our employee example illustrates the concept of polymorphic code. Imagine a large system with a great many different kinds of employees . How will you write and maintain code that deals with all these different employee types?

A traditional approach is to have a "type field" in an employee structure. Then code that manipulates an employee can key off of this type field to determine the correct processing to perform, perhaps using a Select Case statement. Although straightforward, this approach can be quite tedious and error prone. Introducing a new kind of employee can require substantial code maintenance, since all code that uses the structure must be upgraded.

Polymorphism can offer a cleaner solution. You organize the different kinds of employees in a class hierarchy, and you structure your program so that you write general-purpose methods that act upon an object reference whose type is that of the base class that does not change. Your code calls virtual methods of the base class. The call will be automatically dispatched to the appropriate derived class, depending on what kind of employee is actually being referenced. If you add new derived classes, the existing client code that only knows about the generalized base class will not have to be changed. This greatly reduces maintenance effort.

You trade off a slight degradation in runtime performance for more reliable code and less development effort.

The program in PolyEmployee\Step1 provides an illustration. The GetPay method is virtual, and methods in the derived class override it. Here is the code for the base class:

 graphics/codeexample.gif ' Employee.vb Public Class Employee    Public Name As String    Public Sub New(ByVal name As String)       Me.Name = name    End Sub  Public Overridable Function GetPay() As Decimal  Return 1D    End Function End Class 

Methods in the derived classes override the virtual method in the base class. Here is the code for SalaryEmployee:

 ' SalaryEmployee.vb Public Class SalaryEmployee  Inherits Employee  Private m_salary As Decimal    ...  Public Overrides Function GetPay() As Decimal  Return m_salary    End Function End Class 

The WageEmployee class provides its own override of GetPay , where pay is calculated differently.

 ' WageEmployee.vb Imports System Public Class WageEmployee  Inherits Employee  Private m_rate As Decimal    Private m_hours As Double    ...  Public Overrides Function GetPay() As Decimal  Return m_rate * Convert.ToDecimal(m_hours)    End Function End Class 

The payoff comes in the client program, which can now call GetPay polymorphically. Here is the code for the test program:

 ' TestPoly.vb Imports System Module TestPoly    Private m_employees() As Employee    Private Const m_MAXEMPLOYEE As Integer = 10    Private m_nextEmp As Integer = 0    Public Sub Main()       m_employees = New Employee(m_MAXEMPLOYEE) {}       AddSalaryEmployee("Amy", 500D)       AddWageEmployee("Bob", 15D, 40)       AddSalaryEmployee("Charlie", 900D)       PayReport()    End Sub    Private Sub AddSalaryEmployee(_     ByVal name As String, ByVal salary As Decimal)       m_employees(m_nextEmp) = _         New SalaryEmployee(name, salary)       m_nextEmp += 1    End Sub    Private Sub AddWageEmployee(_     ByVal name As String, _     ByVal rate As Decimal, _     ByVal hours As Double)       m_employees(m_nextEmp) = _          New WageEmployee(name, rate, hours)       m_nextEmp += 1    End Sub    Private Sub PayReport()       Dim i As Integer       For i = 0 To m_nextEmp - 1          Dim emp As Employee = m_employees(i)          Dim name As String = emp.Name.PadRight(10)          Dim pay As String = _             String.Format("{0:C}",  emp.GetPay  ())          Dim str As String = name + pay          Console.WriteLine(str)       Next    End Sub End Module 

Here is the output:

 Amy       0.00 Bob       0.00 Charlie   0.00 

Abstract Classes

Sometimes it does not make sense to instantiate a base class. Instead, the base class is used to define a standard template to be followed by the various derived classes. Such a base class is said to be abstract , and it cannot be instantiated. In VB.NET you can designate a base class as abstract by using the keyword MustInherit . The compiler will then flag an error if you try to instantiate the class.

An abstract class may have abstract methods, which are not implemented in the class but only in derived classes. The purpose of an abstract method is to provide a template for polymorphism. The method is called through an object reference to the abstract class, but at runtime the object reference will actually be referring to one of the concrete derived classes. The keyword MustOverride is used to declare abstract methods in an abstract class. An abstract method has no body, and it is not terminated with the regular End Function or End Sub keywords.

An abstract class can be used to provide a cleaner solution of our polymorhphic payroll example. In the Step 1 solution we discussed previously, there was a virtual function GetPay in the base class that returned an arbitrary amount of $1.00, which is really quite silly and artificial. We know that this method will never actually be called, and it is going to be overridden. In fact, the Employee class will itself never be instantiated. Hence we make Employee an abstract class by marking it with the MustInherit keyword, and we make GetPay an abstract method by marking it with the MustOverride keyword. This solution is illustrated in PolyEmployee\Step2 . Note that the GetPay method in the Employee class now does not have a silly, artificial implementation. In fact, it has no implementation at all!

 graphics/codeexample.gif ' Employee.vb Imports System  Public MustInherit Class Employee  Public Name As String    Public Sub New(ByVal name As String)       Me.Name = name    End Sub  Public MustOverride Function GetPay() As Decimal  End Class 

Non-Inheritable Classes

At the opposite end of the spectrum from abstract classes are non-inheritable (also known as sealed ) classes. While you must derive from an abstract class, you cannot derive from a non-inheritable class. A non-inheritable class provides functionality that you can use as is, but you cannot derive from the class and hide or override any of its methods. An example in the .NET Framework class library of a non-inheritable class is System.String .

Marking a class as non-inheritable protects against unwarranted class derivations . It can also make the code a little more efficient, because any virtual functions inherited by the sealed class are automatically treated by the compiler as nonvirtual.

In VB.NET you use the NotInheritable keyword to mark a class as non-inheritable.

Heterogeneous Collections

A class hierarchy can be used to implement heterogeneous collections that can be treated polymorphically. For example, you can create an array whose element type is that of a base class. Then you can store within this array object references whose type is the base class, but which actually may refer to instances of various derived classes in the hierarchy. You may then iterate through the array and call on virtual methods. The appropriate method, as determined by late binding, will be called for each object in the array.

The program PolyEmployee example illustrates a heterogeneous array of three employees, which are a mixture of salary and wage employees.


Team-Fly    
Top
 


Application Development Using Visual BasicR and .NET
Application Development Using Visual BasicR and .NET
ISBN: N/A
EAN: N/A
Year: 2002
Pages: 190

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net