Demonstrating Polymorphic Behavior
Section 10.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 straightforwardbase 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.
In the next example, we aim a base class reference at a derived class object. We then show how invoking a method on a derived class object via a base class reference invokes the derived class functionalitythe type of the actual referenced object, not the type of the reference, determines which method is called. This example demonstrates the key concept that an object of a derived class can be treated as an object of its base class. This enables various interesting manipulations. An application can create an array of base class references that refer to objects of many derived class types. This is allowed 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 CommissionEmployee variable because a BasePlusCommissionEmployee is a CommissionEmployeeso 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 assign the reference of a CommissionEmployee object to a derived class BasePlusCommissionEmployee variable because a CommissionEmployee is not a BasePlusCommissionEmployeea CommissionEmployee does not, for example, have a baseSalary instance variable and does not have a BaseSalary property. The is-a relationship applies from a derived class to its direct and indirect base classes, but not vice versa.
It turns out that the compiler does allow the assignment of a base class reference to a derived class variable if we explicitly cast the base class reference to the derived class typea technique we discuss in greater detail in Section 11.5.6. Why would we ever want to perform such an assignment? A base class reference can be used to invoke only the methods declared in the base classattempting to invoke derived-class-only methods through a base class reference results in compilation errors. If an application needs to perform a derived-class-specific operation on a derived class object referenced by a base class variable, the application must first cast the base class reference to a derived class reference through a technique known as downcasting. This enables the application to invoke derived class methods that are not in the base class. We present a concrete example of downcasting in Section 11.5.6.
The example in Fig. 11.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 straightforwardas in Section 10.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 application uses classes CommissionEmployee3 and BasePlusCommissionEmployee4 from Fig. 10.13 and Fig. 10.14, respectively.]
Figure 11.1. Assigning base class and derived class references to base class and derived class variables.
(This item is displayed on pages 513 - 514 in the print version)
1 // Fig. 11.1: PolymorphismTest.cs 2 // Assigning base class and derived class references to base class and 3 // derived class variables. 4 using System; 5 6 public class PolymorphismTest 7 { 8 public static void Main( string[] args ) 9 { 10 // assign base class reference to base class variable 11 CommissionEmployee3 commissionEmployee = new CommissionEmployee3( 12 "Sue", "Jones", "222-22-2222", 10000.00M, .06M ); 13 14 // assign derived class reference to derived class variable 15 BasePlusCommissionEmployee4 basePlusCommissionEmployee = 16 new BasePlusCommissionEmployee4( "Bob", "Lewis", 17 "333-33-3333", 5000.00M, .04M, 300.00M ); 18 19 // invoke ToString and Earnings on base class object 20 // using base class variable 21 Console.WriteLine( "{0} {1}: {2} {3}: {4:C} ", 22 "Call CommissionEmployee3's ToString with base class reference", 23 "to base class object", commissionEmployee.ToString(), 24 "earnings", commissionEmployee.Earnings() ); 25 26 // invoke ToString and Earnings on derived class object 27 // using derived class variable 28 Console.WriteLine( "{0} {1}: {2} {3}: {4:C} ", 29 "Call BasePlusCommissionEmployee4's ToString with derived class", 30 "reference to derived class object", 31 basePlusCommissionEmployee.ToString(), 32 "earnings", basePlusCommissionEmployee.Earnings() ); 33 34 // invoke ToString and Earnings on derived class object 35 // using base class variable 36 CommissionEmployee3 commissionEmployee2 = 37 basePlusCommissionEmployee; 38 Console.WriteLine( "{0} {1}: {2} {3}: {4:C}", 39 "Call BasePlusCommissionEmployee4's ToString with base class", 40 "reference to derived class object", 41 commissionEmployee2.ToString(), "earnings", 42 commissionEmployee2.Earnings() ); 43 } // end Main 44 } // end class PolymorphismTest
|
In Fig. 11.1, lines 1112 create a new CommissionEmployee3 object and assign its reference to a CommissionEmployee3 variable. Lines 1517 create a new BasePlusCommissionEmployee4 object and assign its reference to a BasePlusCommissionEmployee4 variable. These assignments are naturalfor example, a CommissionEmployee3 variable's primary purpose is to hold a reference to a CommissionEmployee3 object. Lines 2124 use the reference commissionEmployee to invoke methods ToString and Earnings. Because commissionEmployee refers to a CommissionEmployee3 object, base class CommissionEmployee3's version of the methods are called. Similarly, lines 2832 use basePlusCommissionEmployee to invoke the methods ToString and Earnings on the BasePlusCommissionEmployee4 object. This invokes derived class BasePlusCommissionEmployee4's version of the methods.
Lines 3637 then assign the reference to derived class object basePlusCommissionEmployee to a base class CommissionEmployee3 variable, which lines 3842 use to invoke methods ToString and Earnings. 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. Hence, commissionEmployee2.ToString() in line 41 actually calls class BasePlusCommissionEmployee4'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 method call made through a variable, the compiler determines if the method can be called by checking the variable's class type. If that class contains the proper method declaration (or inherits one), the compiler allows the call to be compiled. At execution time, the type of the object to which the variable refers determines the actual method to use.
Abstract Classes and Methods
|