Visual Basic 2005 for Programmers (2nd Edition)

10.5. Constructors in Derived Classes

As we explained in the preceding section, instantiating a derived class object begins a chain of constructor calls in which the derived class constructor, before performing its own tasks, invokes its direct base class's constructor either explicitly (via the MyBase reference) or implicitly (calling the base class's default or parameterless constructor). Similarly, if the base class is derived from another class (as is every class except Object), the base class constructor invokes the constructor of the next class up the hierarchy, and so on. The last constructor called in the chain is always the constructor for class Object. The original derived class constructor's body finishes executing last. Each base class's constructor manipulates the base class instance variables that the derived class object inherits. For example, let's reconsider the CommissionEmployeeBasePlusCommissionEmployee hierarchy from Fig. 10.13 and 10.14. When a program creates a BasePlusCommissionEmployee object, the BasePlusCommissionEmployee constructor is called. That constructor, before executing its full body code, immediately calls CommissionEmployee's constructor, which in turn calls Object's constructor. Class Object's constructor has an empty body, so it immediately returns control to the CommissionEmployee's constructor, which then initializes the Private instance variables of CommissionEmployee that are part of the BasePlusCommissionEmployee object. When the CommissionEmployee's constructor completes execution, it returns control to the BasePlusCommissionEmployee's constructor, which initializes the BasePlusCommissionEmployee object's baseSalaryValue.

Our next example revisits the commission employee hierarchy by redeclaring class CommissionEmployee (Fig. 10.16) and class BasePlusCommissionEmployee (Fig. 10.17) with each class's constructor printing a message when invoked, enabling us to observe the order in which the constructors in the hierarchy execute.

Figure 10.16. CommissionEmployee's constructor outputs text.

1 ' Fig. 10.16: CommissionEmployee.vb 2 ' CommissionEmployee class represents a commission employee. 3 Public Class CommissionEmployee 4 Private firstNameValue As String ' first name 5 Private lastNameValue As String ' last name 6 Private socialSecurityNumberValue As String ' social security number 7 Private grossSalesValue As Decimal ' gross weekly sales 8 Private commissionRateValue As Decimal ' commission percentage 9 10 ' five-argument constructor 11 Public Sub New(ByVal first As String, ByVal last As String, _ 12 ByVal ssn As String, ByVal sales As Decimal, ByVal rate As Decimal) 13 14 ' implicit call to Object constructor occurs here 15 FirstName = first 16 LastName = last 17 SocialSecurityNumber = ssn 18 GrossSales = sales ' validate and store gross sales 19 CommissionRate = rate ' validate and store commission rate 20 Console.WriteLine(vbCrLf & _ 21 "CommissionEmployee constructor:" & vbCrLf & "{0}" , Me) 22 End Sub ' New 23 24 ' property FirstName 25 Public Property FirstName() As String 26 Get 27 Return firstNameValue 28 End Get 29 30 Set(ByVal first As String) 31 firstNameValue = first ' no validation 32 End Set 33 End Property ' FirstName 34 35 ' property LastName 36 Public Property LastName() As String 37 Get 38 Return lastNameValue 39 End Get 40 41 Set(ByVal last As String) 42 lastNameValue = last ' no validation 43 End Set 44 End Property ' LastName 45 46 ' property SocialSecurityNumber 47 Public Property SocialSecurityNumber() As String 48 Get 49 Return socialSecurityNumberValue 50 End Get 51 52 Set(ByVal ssn As String) 53 socialSecurityNumberValue = ssn ' no validation 54 End Set 55 End Property ' SocialSecurityNumber 56 57 ' property GrossSales 58 Public Property GrossSales() As Decimal 59 Get 60 Return grossSalesValue 61 End Get 62 63 Set(ByVal sales As Decimal) 64 If sales < 0.0 Then ' validate gross sales 65 grossSalesValue = 0 66 Else 67 grossSalesValue = sales 68 End If 69 End Set 70 End Property ' GrossSales 71 72 ' property CommissionRate 73 Public Property CommissionRate() As Decimal 74 Get 75 Return commissionRateValue 76 End Get 77 78 Set(ByVal rate As Decimal) 79 If rate > 0.0 AndAlso rate < 1.0 Then ' validate rate 80 commissionRateValue = rate 81 Else 82 commissionRateValue = 0 83 End If 84 End Set 85 End Property ' CommissionRate 86 87 ' calculate earnings 88 Public Overridable Function CalculateEarnings() As Decimal 89 Return CommissionRate * GrossSales 90 End Function ' CalculateEarnings 91 92 ' return String representation of CommissionEmployee object 93 Public Overrides Function ToString() As String 94 Return ("commission employee: " & FirstName & " " & _ 95 LastName & vbCrLf & "social security number: " & _ 96 SocialSecurityNumber & vbCrLf & "gross sales: " & _ 97 String.Format("{0:C}", GrossSales) & vbCrLf & _ 98 "commission rate: " & String.Format("{0:F}", CommissionRate)) 99 End Function ' ToString 100 End Class ' CommissionEmployee

Figure 10.17. BasePlusCommissionEmployee's constructor outputs text.

1 ' Fig. 10.17: BasePlusCommissionEmployee.vb 2 ' BasePlusCommissionEmployee inherits from class CommissionEmployee. 3 Public Class BasePlusCommissionEmployee 4 Inherits CommissionEmployee 5 6 Private baseSalaryValue As Decimal ' base salary per week 7 8 ' six-argument constructor 9 Public Sub New(ByVal first As String, ByVal last As String, _ 10 ByVal ssn As String, ByVal sales As Decimal, _ 11 ByVal rate As Decimal, ByVal salary As Decimal) 12 13 ' use MyBase reference to CommissionEmployee constructor explicitly 14 MyBase.New(first, last, ssn, sales, rate) 15 BaseSalary = salary ' validate and store base salary 16 Console.WriteLine(vbCrLf & _ 17 "BasePlusCommissionEmployee constructor:" & vbCrLf & "{0}", Me) 18 End Sub ' New 19 20 ' property BaseSalary 21 Public Property BaseSalary() As Decimal 22 Get 23 Return baseSalaryValue 24 End Get 25 26 Set(ByVal salary As Decimal) 27 If salary < 0.0 Then ' validate base salary 28 baseSalaryValue = 0 29 Else 30 baseSalaryValue = salary 31 End If 32 End Set 33 End Property ' BaseSalary 34 35 ' calculate earnings 36 Public Overrides Function CalculateEarnings() As Decimal 37 Return BaseSalary + MyBase.CalculateEarnings() 38 End Function ' earnings 39 40 ' return String representation of BasePlusCommissionEmployee object 41 Public Overrides Function ToString() As String 42 Return ("base-plus-" & MyBase.ToString() & vbCrLf & _ 43 "base salary: " & String.Format("{0:C}", BaseSalary)) 44 End Function ' ToString 45 End Class ' BasePlusCommissionEmployee

Class CommissionEmployee (Fig. 10.16) contains the same features as the version of the class shown in Fig. 10.13. We have modified the constructor (lines 1122) to output text upon its invocation (lines 2021). Note that outputting Me with the {0} format string (line 21) implicitly invokes the ToString method of the CommissionEmployee object being constructed to obtain the object's string representation.

Class BasePlusCommissionEmployee (Fig. 10.17) is almost identical to Fig. 10.14, except that BasePlusCommissionEmployee's constructor also outputs text when invoked (lines 1617). Again, we output Me using the {0} format string (line 17) to implicitly invoke the ToString method, this time to obtain the BasePlusCommissionEmployee object's string representation.

Figure 10.18 demonstrates the order in which constructors are called for objects of classes that are part of an inheritance hierarchy. Method Main begins by instantiating CommissionEmployee object employee1 (lines 67). Next, lines 1011 instantiate BasePlusCommissionEmployee object employee2. This invokes the CommissionEmployee constructor, which prints output with the values passed from the BasePlusCommissionEmployee constructor, then performs the output specified in the BasePlusCommissionEmployee constructor.

Figure 10.18. Constructor call order using Me.

1 ' Fig. 10.18: Constructor.vb 2 ' Display order in which base class and derived class constructors 3 ' and finalizers are called. 4 Module Constructor 5 Sub Main() 6 Dim employee1 As New CommissionEmployee( _ 7 "Bob", "Lewis", "333-33-3333", 5000, 0.04D) 8 Console.WriteLine() 9 10 Dim employee2 As New BasePlusCommissionEmployee( _ 11 "Lisa", "Jones", "555-55-5555", 2000, 0.06D, 800) 12 End Sub ' Main 13 End Module ' Constructor

CommissionEmployee constructor: commission employee: Bob Lewis social security number: 333-33-3333 gross sales: $5,000.00 commission rate: 0.04 CommissionEmployee constructor: base-plus-commission employee: Lisa Jones social security number: 555-55-5555 gross sales: $2,000.00 commission rate: 0.06 base salary: $0.00 BasePlusCommissionEmployee constructor: base-plus-commission employee: Lisa Jones social security number: 555-55-5555 gross sales: $2,000.00 commission rate: 0.06 base salary: $800.00

Analyzing the Output

The outputs of the CommissionEmployee constructor and BasePlusCommissionEmployee constructor calls each contain values for the first name, last name, social security number, gross sales, commission rate and base salary of the BasePlusCommissionEmployee. When constructing a BasePlusCommissionEmployee object, the Me reference used in the body of both the CommissionEmployee and BasePlusCommissionEmployee constructors refers to the BasePlusCommissionEmployee object being constructed. When a program invokes method ToString on an object, the version of ToString that executes is always the version defined in that object's class. This is an example of polymorphism, a key aspect of object-oriented programming that we discuss in detail in Chapter 11. Reference Me refers to the current BasePlusCommissionEmployee object being constructed, so BasePlusCommissionEmployee's ToString method executes even when ToString is invoked from the body of class CommissionEmployee's constructor. This would not be the case if the CommissionEmployee constructor were called to initialize a new CommissionEmployee object. When the CommissionEmployee constructor invokes method ToString for the BasePlusCommissionEmployee being constructed, the program displays 0 for the BaseSalary value, because the BasePlusCommissionEmployee constructor's body has not yet initialized the BaseSalary. The BasePlusCommissionEmployee constructor output shows the proper BaseSalary value (i.e., 800.00), because this line is output after the BaseSalary is initialized.

Using MyClass in Class CommissionEmployee's Constructor

To force CommissionEmployee's ToString method to execute when the CommissionEmployee's constructor is called, we use the MyClass reference. Reference MyClass is similar to Me, except that a method call with MyClass always invokes the version of the method defined in that particular classthe method called is not affected by the runtime type of the object. For example, if we replace Me in line 21 of Fig. 10.16 with

MyClass.ToString()

then CommissionEmployee's ToString will be executed (even though the runtime type of the object is BasePlusCommissionEmployee). Thus, the output of the CommissionEmployee's constructor call will not display the base salary value (Fig. 10.19).

Figure 10.19. Constructor call order using MyClass.

CommissionEmployee constructor: commission employee: Bob Lewis social security number: 333-33-3333 gross sales: $5,000.00 commission rate: 0.04 CommissionEmployee constructor: commission employee: Lisa Jones social security number: 555-55-5555 gross sales: $2,000.00 commission rate: 0.06 BasePlusCommissionEmployee constructor: base-plus-commission employee: Lisa Jones social security number: 555-55-5555 gross sales: $2,000.00 commission rate: 0.06 base salary: $800.00

Recall that Shared class variables and Shared class methods exist independently of any class objects. They also exist when there are no objects of that class. Hence, the MyClass reference cannot be used in a Shared method because such a method can be called even when there are no objects of the corresponding class.

Категории