Visual C# 2012 How to Program
Polymorphism enables you to write apps that process
objects that share the same base class in a class
hierarchy as if they were all objects of the base class.
Polymorphism promotes extensibility.
If class Rectangle is derived from class Quadrilateral,
then a Rectangle is a more specific version of a
Quadrilateral.
Any operation that can be performed on a Quadrilateral
object can also be performed on a Rectangle object.
These operations also can be performed on other
Quadrilaterals, such as Squares, Parallelograms
and Trapezoids.
The polymorphism occurs when an app invokes a method
through a base-class variable.
As another example, suppose we design a video game that
manipulates objects of many different types, including objects
of classes Martian, Venusian, Plutonian,
SpaceShip and LaserBeam.
Each class inherits from the common base class
SpaceObject, which contains method Draw.
A screen-manager app maintains a collection (e.g., a
SpaceObject array) of references to objects of the various
classes.
To refresh the screen, the screen manager periodically sends
each object the same message—namely, Draw, while object
responds in a unique way.
In a method call on an object, the type of the
actual referenced object
,
not the type of the
reference
, determines which method is called.
An object of a derived class can be treated as an object of its base class.
A base-class object is not an object of any of its derived classes.
The
is-a
relationship applies from a derived class to its direct and
indirect base classes, but not vice versa.
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.
If an app needs to perform a derived-class-specific operation on
a derived-class object referenced by a base-class variable, the
app must first cast the base-class reference to a derived-class
reference through a technique known as downcasting. This
enables the app to invoke derived-class methods that are not in
the base class.
Fig. 12.1 demonstrates three ways to use base-class and derived-
class variables.
When the compiler encounters a virtual method call made through a
variable, the compiler checks the
variables
class type to determines if
the method can be called.
At execution time,
the type of the object to which the variable refers
determines the actual method to use.
Abstract classes, or abstract base classes cannot be
used to instantiate objects.
Abstract base classes are too general to create real
objectsthey specify only what is common among
derived classes.
Purpose of an Abstract Class
Classes that can be used to instantiate objects are
called concrete classes.
Concrete classes provide the specifics that make it
reasonable to instantiate objects.
Creating an Abstract Class
An abstract class normally contains one or more abstract methods,
which have the keyword abstract in their declaration.
A class that contains abstract methods must be declared as an abstract
class even if it contains concrete (non-
abstract) methods.
Abstract methods do not provide implementations.
Abstract Properties
Abstract property declarations have the form:
public abstract
PropertyType MyProperty
{
get;
set;
} // end abstract property
An abstract property omits implementations for the get
accessor and/or the set accessor.
Concrete derived classes must provide implementations for
every
accessor declared in the abstract property.
Constructors and static Methods Cannot be
Abstract
Constructors and static methods
cannot
be
declared abstract.
We can use abstract base classes to declare variables
that can hold references to objects of any concrete
classes derived from those abstract classes.
You can use such variables to manipulate derived-
class objects polymorphically and to invoke static
methods declared in those abstract base classes.
In this section, we create an enhanced employee hierarchy to
solve the following problem:
A company pays its employees on a weekly basis. The employees are of
four types: Salaried employees are paid a fixed weekly salary regardless
of the number of hours worked, hourly employees are paid by the hour
and receive "time-and-a-half" overtime pay for all hours worked in
excess of 40 hours, commission employees are paid a percentage of their
sales, and salaried-commission employees receive a base salary plus a
percentage of their sales. For the current pay period, the company has
decided to reward salaried-commission employees by adding 10% to
their base salaries. The company wants to implement an app that
performs its payroll calculations polymorphically.
We use abstract class Employee to represent the general concept of an
employee.
SalariedEmployee, CommissionEmployee and HourlyEmployee
extend Employee.
Class BasePlusCommissionEmployeewhich extends
CommissionEmployeerepresents the last employee type.
The UML class diagram in Fig. 12.2 shows the inheritance
hierarchy for our polymorphic employee payroll app.
Class Employee provides methods Earnings and
ToString, in addition to the properties that manipulate
Employees instance variables.
Each earnings calculation depends on the employees class, so
we declare Earnings as abstract.
The app iterates through the array and calls method
Earnings for each Employee object. These method calls
are processed polymorphically.
Each derived class overrides method ToString to create a
string representation of an object of that class.
The diagram in Fig. 12.3 shows each of the five
classes in the hierarchy down the left side and
methods Earnings and ToString across the top.
The Employee classs declaration is shown in
Fig. 12.4.
The SalariedEmployee classs declaration is shown
in Fig. 12.5.
The HourlyEmployee classs declaration is shown in
Fig. 12.6.
The CommissionEmployee classs declaration is
shown in Fig. 12.7.
Class BasePlusCommissionEmployee
(Fig. 12.8) extends class CommissionEmployee
and therefore is an indirect derived class of class
Employee.
The app in Fig. 12.9 tests our Employee hierarchy.
You can avoid a potential InvalidCastException by
using the as operator to perform a downcast rather than a cast
operator.
If the downcast is invalid, the expression will be null instead of throwing an
exception.
Method GetType returns an object of class Type
(of namespace System), which contains information about the
object’s type, including its class name, the names of its
methods, and the name of its base class.
The Type classs ToString method returns the class name.
12.5.7 Summary of the Allowed Assignments Between
Base-Class and Derived-Class Variables
Assigning a base-class reference to a base-class variable is
straightforward.
Assigning a derived-class reference to a derived-class variable is
straightforward.
Assigning a derived-class reference to a base-class variable is safe,
because the derived-class object
is an
object of its base class.
However, this reference can be used to refer only to base-class
members.
Attempting to assign a base-class reference to a derived-class variable
is a compilation error. To avoid this error, the base-class reference
must be cast to a derived-class type explicitly.