12.3 Demonstrating Polymorphic Behavior

Section 11.4 created a commission-employee class hierarchy, in which class BasePlusCommissionEmployee inherited from class CommissionEmployee. The examples in that section manipulated CommissionEmployee and BasePlusCommissionEmployee objects by using references to them to invoke their methods. We aimed base-class references at base-class objects and derived-class references at derived-class objects. These assignments are natural and straightforward—base-class references are intended to refer to base-class objects, and derived-class references are intended to refer to derived-class objects. However, other assignments are possible.

The next example aims a base-class reference at a derived-class object, then shows how invoking a method on a derived-class object via a base-class reference invokes the derived-class functionality—the type of the actual referenced object, not the type of the reference, determines which method is called. This demonstrates the key concept that a derived-class object can be treated as an object of its base class, which enables various interesting manipulations. An app can create a collection of base-class references that refer to objects of many derived-class types, because each derived-class object is an object of its base class. For instance, we can assign the reference of a BasePlusCommissionEmployee object to a base-class Commission-Employee variable because a BasePlusCommissionEmployee is a CommissionEmployee—so we can treat a BasePlusCommissionEmployee as a CommissionEmployee.

A base-class object is not an object of any of its derived classes. For example, we cannot directly assign the reference of a CommissionEmployee object to a derived-class Base-PlusCommissionEmployee variable, because a CommissionEmployee is not a BasePlusCommissionEmployee—a CommissionEmployee does not, for example, have a baseSalary instance variable and does not have a BaseSalary property. The compiler allows the assignment of a base-class reference to a derived-class variable if we explicitly cast the base-class reference to the derived-class type—a technique we discuss in greater detail in Section 12.5.6.

Software Engineering Observation 12.2

The is-a relationship applies from a derived class to its direct and indirect base classes, but not vice versa.

Figure 12.1 demonstrates three ways to use base-class and derived-class variables to store references to base-class and derived-class objects. The first two are straightforward— as in Section 11.4, we assign a base-class reference to a base-class variable, and we assign a derived-class reference to a derived-class variable. Then we demonstrate the relationship between derived classes and base classes (i.e., the is-a relationship) by assigning a derived-class reference to a base-class variable. [Note: This app uses classes CommissionEmployee and BasePlusCommissionEmployee from Fig. 11.10 and Fig. 11.11, respectively.]

Fig. 12.1 Assigning base-class and derived-class references to base-class and derived-class variables.

Alternate View

  1   // Fig. 12.1: PolymorphismTest.cs
  2   // Assigning base-class and derived-class references to base-class and
  3   // derived-class variables.
  4   using System;
  5   
  6   class PolymorphismTest
  7   {
  8     static void Main()
  9     {
 10        // assign base-class reference to base-class variable 
 11        var commissionEmployee = new CommissionEmployee(   
 12           "Sue", "Jones", "222-22-2222", 10000.00M, .06M);
 13  
 14        // assign derived-class reference to derived-class variable 
 15        var basePlusCommissionEmployee = new BasePlusCommissionEmployee(
 16           "Bob", "Lewis", "333-33-3333", 5000.00M, .04M, 300.00M);     
 17  
 18        // invoke ToString and Earnings on base-class object
 19        // using base-class variable 
 20        Console.WriteLine(
 21           "Call CommissionEmployee's ToString and Earnings methods " +
 22           "with base-class reference to base class object
");
 23        Console.WriteLine(commissionEmployee.ToString());
 24        Console.WriteLine($"earnings: {commissionEmployee.Earnings()}
");
 25  
 26        // invoke ToString and Earnings on derived-class object
 27        // using derived-class variable
 28        Console.WriteLine("Call BasePlusCommissionEmployee's ToString and" +
 29          " Earnings methods with derived class reference to" +
 30          " derived-class object
");
 31        Console.WriteLine(basePlusCommissionEmployee.ToString());
 32        Console.WriteLine(
 33           $"earnings: {basePlusCommissionEmployee.Earnings()}
");
 34  
 35        // invoke ToString and Earnings on derived-class object
 36        // using base-class variable 
 37        CommissionEmployee commissionEmployee2 = basePlusCommissionEmployee;
 38        Console.WriteLine(
 39           "Call BasePlusCommissionEmployee's ToString and Earnings " +
 40           "methods with base class reference to derived-class object");
 41        Console.WriteLine(commissionEmployee2.ToString());
 42        Console.WriteLine(
 43           $"earnings: {basePlusCommissionEmployee.Earnings()}
");
 44     }
 45   }

Call CommissionEmployee's ToString and Earnings methods with base class
reference to base class object:

commission employee: Sue Jones
social security number: 222-22-2222
gross sales: $10,000.00
commission rate: 0.06
earnings: $600.00

Call BasePlusCommissionEmployee's ToString and Earnings methods with derived
class reference to derived class object:

base-salaried commission employee: Bob Lewis
social security number: 333-33-3333
gross sales: $5,000.00
commission rate: 0.04
base salary: $300.00
earnings: $500.00

Call BasePlusCommissionEmployee's ToString and Earnings methods with base
class reference to derived class object:

base-salaried commission employee: Bob Lewis
social security number: 333-33-3333
gross sales: $5,000.00
commission rate: 0.04
base salary: $300.00
earnings: $500.00

In Fig. 12.1, lines 11–12 create a new CommissionEmployee object and assign its reference to a CommissionEmployee variable and lines 15–16 create a new BasePlusCommissionEmployee object and assign its reference to a BasePlusCommissionEmployee variable. These assignments are natural—a CommissionEmployee variable’s primary purpose is to hold a reference to a CommissionEmployee object. Lines 23–24 use the reference commissionEmployee to invoke methods ToString and Earnings. Because commissionEmployee refers to a CommissionEmployee object, base class CommissionEmployee’s version of the methods are called. Similarly, lines 31–33 use the reference basePlusCommissionEmployee to invoke the methods ToString and Earnings on the BasePlusCommissionEmployee object. This invokes derived class BasePlusCommissionEmployee’s version of the methods.

Line 37 then assigns the reference to derived-class object basePlusCommissionEmployee to a base-class CommissionEmployee variable, which lines 41–43 use to invoke methods ToString and Earnings. Note that the call commissionEmployee2.ToString() in line 41 actually calls derived class BasePlusCommissionEmployee’s ToString method. The compiler allows this “crossover” because an object of a derived class is an object of its base class (but not vice versa). When the compiler encounters a virtual method call made through a variable, the compiler checks the variable’s class type to determine if the method can be called. If that class contains the proper method declaration (or inherits one), the call compiles. At execution time, the type of the object to which the variable refers determines the actual method to use.

Software Engineering Observation 12.3

A base-class variable that contains a reference to a derived-class object and is used to call a virtual method actually calls the overriding derived-class version of the method.

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

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