34
Under construction CIS 3309 - Lecture Set E - Classes (Ver 18, April 11, 2020) ============================================================== Fundamentals of Inheritance Go to: Section E.1 – Introduction Section E.2 – Inheriting from a Class Section E.3 – Base Classes in the .NET Framework Section E.4 – Inheriting Base Class Members – Fields, Methods, Properties Section E.5 – Encapsulation and Designing with Classes and Inheritance Section E.6 – Inheritance and Constructors Section E.7 – Polymorphism and Type Substitution Section E.8 – Replacing Methods in a Derived Class Section E.9 -- Summary Appendix E – Inheritance 7/8/2022 5:49:47 AM Page 1

Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

  Under construction

CIS 3309 - Lecture Set E - Classes(Ver 18, April 11, 2020)

==============================================================

 Fundamentals of Inheritance 

Go to:      Section E.1 – IntroductionSection E.2 – Inheriting from a ClassSection E.3 – Base Classes in the .NET Framework Section E.4 – Inheriting Base Class Members – Fields, Methods, PropertiesSection E.5 – Encapsulation and Designing with Classes and InheritanceSection E.6 – Inheritance and ConstructorsSection E.7 – Polymorphism and Type Substitution   Section E.8 – Replacing Methods in a Derived ClassSection E.9 -- Summary                     

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 1

Page 2: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

E. 1 Introduction

Inheritance is one of the most significant design features of OOP. Inheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since then, new languages (e.g., Java, Objective C, C#.NET, VB .NET, Python) have come along and refined the features and syntax for using inheritance.

The .NET Framework makes extensive use of inheritance. The CTS relies heavily on inheritance. When you use the Windows Forms

package, your new forms will inherit from an existing form-based class in the FCL. When you use ASP.NET, your Web pages and Web services will inherit from

preexisting classes in the FCL. As a developer building applications or component libraries based on the .NET

Framework, you will find that familiarity with inheritance is an absolute necessity.

Why Bother with inheritance? In fact, why bother with any higher-level language features, such as looping and decision structures, try-catch, methods, classes, etc?

E.2 Inheriting from a Class

Inheritance allows you to derive new classes from an existing ones, thereby avoiding duplication, and enabling us reuse and enhance existing software components as suggested for entities in a particular problem domain.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 2

The goal with all of these language features is to provide programmers with tools that are intended to make all aspects of software life-cycle, from design, to testing, coding, debugging, and maintenance, easier and more efficient (and less costly). It is generally agreed that software development using features found in most object-oriented languages should enable us to better model problem domain entities and processes, helping to improve the development process. Do we need these language features to develop our systems? No.* Are they believed to be useful over the entire life-cycle of a software product? Many developers think they are.

Page 3: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

Definitions (Terminology): Suppose that class C inherits from class B. Then class B is typically called the base class, and class C is called the derived class. (Some refer to B and C as superclass and subclass, or as parent and child classes, respectively.

In this material, we will do our best to use the terms "base" and "derived."

Contracts – It is useful to think of a class as describing a contract between a client (the class) and objects of the class. In addition, any derived class definition automatically inherits the contract of its base class and extends that contract.

Another kind of inheritance, inheritance by Specification is also important, but will not be discussed here.

One of the primary reasons for designing with inheritance is that it provides an effective means for reusing code. For example, you can define a set of fields (attributes), methods, and properties in one class, and then use inheritance to reuse this code across several other classes.

Inheritance is particularly beneficial in scenarios that require multiple classes with much in common but in which the classes are all specialized in slightly different ways.

Examples include the following familiar categories: employees (person, administrative, technical, sales), cycles (bicycle, tricycle, unicycle, motorcycle), and graphical shapes (circle, rectangle, triangle).

Example 1: A simple base class

// Base class public class Person { private string hiddenName; // Person's full name private int hiddenAge; // Person's age

// Parameterless constructor public Person () { hiddenName = ""; hiddenAge = 0; } // end Parameterless Constructor

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 3

Inheritance by Specialization --

The idea is that the derived class starts with a reusable class definition and modifies it by adding more members or by changing the behavior of existing methods and properties.

Page 4: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

// Parameterized constructor public Person (string n, int a) { hiddenName = n; hiddenAge = a; } // end parameterized constructor

. . .

} // end Person Class End Class

Basic syntax of inheritance -- Example 1 (above) illustrates the basic syntax a class named Person that we would like to use as a base class. In C#, when you want to state explicitly that one class inherits from another, you follow the name of the class with a colon and the name of the base class. Below is an illustration of the basic syntax of inheritance: a derived class definition for a Client using Person as its base class:

public class Student : Person { private string hiddenStudentID; private string hiddenMajor; . . . // Parameterless constructor public Student() { hiddenStudentID = ""; hiddenMajor = ""; } // end Parameterless constructor

// Parameterized constructor (with 1 argument) // Calls the base class first, passing it name

public Student (string ID): base (ID) { hiddenStudentID = "903041234"; hiddenMajor = ""; } // end Parameterized Constructor (with 1 argument) . . . } // end Class

In this example,

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 4

Page 5: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

The Person class serves as a base class for the Student class. This Student class and any other classes derived from Person will always share

a common set of implementation details, and all will support a unified programming contract defined by the Person class. (But it is also possible to write implementations for these derived classes that are quite different from each other as well as to add new members to each class to further increase their specialization, as you will see as you proceed with your own project.)

By default, every class you create in C#.NET can be inherited by other classes. If for some reason you don't want other programmers to inherit from your class, you can create a sealed class. A sealed class is defined in the C# .NET language using the keyword sealed. The compiler will generate a compile-time error whenever a programmer attempts to define a class that inherits from such a sealed class:

// Sealed class definitionpublic sealed class Programmer { // Sealed class implementation . . .} // End Class

public class Programmer : Genius // compile-time error!

Figure E.1 shows a design view of class definitions known as an inheritance hierarchy.

Figure E.1 - An inheritance hierarchy

The Person class is located at the top of the hierarchy. The Client and Employer classes inherit from the Person class and are

located directly below it in the inheritance hierarchy. Notice the direction of the arrows when denoting inheritance. Manager and Worker have been defined with Employee as their base class.

They inherit indirectly from the Person class. Thus a class in a multilevel inher-

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 5

Person

Client Employee

Manager Worker

Page 6: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

itance hierarchy inherits from every class reachable by following the inheritance arrows upward.

For example, you can correctly say that a programmer "is a" person. You can also correctly say that a senior programmer "is a" programmer.

The purpose of the “is-a” test is to ensure that a derived class is designed to model a more specialized version of whatever entity the base class is modeling.

MODEL is the key term here!! All classes model something. And, virtually everything we do in writing programs involves constructing models of processes and entities.

You should try never to establish an inheritance relationship between two classes that cannot pass the “is-a” test.

Example 2: Suppose you have a cycle class hierarchy with a cycle at the top and a unicycle, bicycle and tricycle (remember those?) all inheriting from the cycle. It might be tempting now to put such items as wheels, tire, handlebars, etc., (also modeled using classes) somewhere in the hierarchy. You should resist this temptation! Wheels, tires, handlebars, etc., cannot pass the "is-a" test for any of the classes just discussed. The three items we just listed should be treated as additional attributes of the cycle entities.

[You can correctly say that a bicycle "has a" wheel (or two), but that relationship calls for a different design technique that doesn't involve inheritance. When you determine that two entities exhibit the "has a" relationship, the situation most likely calls for a design using containment, in which the Wheel class (for example) is used to define fields (attributes) within the Bicycle class (for example). In other words, the Bicycle class would likely contain one or more wheel objects (instances of the Wheel class.]

E. 3 Base Classes in the .NET Framework

Now that you've seen some basic principles and the syntax for using inheritance, it's time to introduce a few important rules that imposed by the .NET Common Type System (CTS).

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 6

Design Rule –

Two classes that will be related through inheritance should be able to pass the is-a test.

-- If it makes sense to say "C is a B," then it makes sense for class C to inherit from class B.

-- If such a sentence doesn't make sense, then you should reconsider the use of inheritance in this situation.

Page 7: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

Example 3: When you define a class without explicitly specifying a base class, the compiler automatically modifies the class definition to inherit from the Object class in the FCL. Note that every structure and every enumeration also has a base type. Recall from our earlier study of data types that the inheritance hierarchy of the CTS is singly rooted because the system-defined Object class serves as the ultimate base type. Every other class either inherits directly from the Object class or inherits from another class that inherits (either directly or indirectly) from the Object class.

E.4 Inheriting Base Class Members – Fields, Methods, Properties

We begin by recalling the levels of class member accessibility discussed in a previous lecture set. These are summarized in the next box.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 7

.NET Rules Governing the use of Inheritance -- You cannot define a class that inherits directly from more than one base class. As is the case with Java and VB, CTS does not support multiple inheritance.You cannot define a class that doesn't have a base class.

Five Levels of Accessibility --

Private. A base class member defined with the Private access modifier is not acces-sible to code outside this class. Thus no other class, derived or otherwise, can directly access this member. Protected. A base class member defined with the Protected access modifier is directly accessible to code inside the defining class and any other class derived from this class. But it is not accessible to any other class. Public. A base class member defined with the Public access modifier is directly accessible to code inside any class. Internal. A member that is defined with the Internal access modifier is accessible to all code inside the containing assembly but inaccessible to code in other assemblies. The internal level of accessibility is not affected by whether the accessing code is part of a derived class.Protected Internal. The protected internal level of accessibility is achieved by combining the Protected access modifier with the Internal access modifier. A protected internal is accessible to all code inside the containing assembly and to code within derived classes (whether the derived class is part of the same assembly or not).

Page 8: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

Keeping these definitions in mind we can briefly summarize how inheritance works. Every public or protected member (field, method, and property) that is part of a

base class definition is inherited by the derived class definition and may be directly accessed (no methods required).

Each object created from a derived type carries with it all the states and behaviors that are defined by its base class.

Code in a derived class has direct access to the public and protected members inherited from its base class. However, access to the private members of the base class must be programmed using the public methods in the base class.

Constructors are not inherited. We will have more to say about constructors in Section E.6. For now, we suggest that you always write at least a base constructor for all of your classes.

What Goes On Behind the Scenes?

Constructors aside for now, one view of how inheritance works is depicted in Figure E.2. As this figure shows, all members (methods and data stores) of the base class are inherited by the derived class (all depicted in the white squares). All rules of accessibility to these inherited base class members are the same as for the base class. Thus, the public and protected members of the base class are directly accessible to the derived class. The private members of the base class are still encapsulated and accessible ONLY through public methods of the base class.

One way to view this is to consider the base class as “becoming part of” the derived class. Remember too, that the derived class may contain its own private, protected, public, etc. members (shown in yellow). In Figure E.2, we indicate this accessibility for both the derived and base classes using a dotted box to enclose public and protected class members. The private members of the base class, and any additional private members of the derived class are enclosed in solid line boxes.

In the EmployeeManagerClient hierarchy illustrated in Figure E.1 this “becoming part of“ concept implies, for example, that the three attributes of a Person become part of a Manager object. Person may be an abstract class (which cannot be instantiated), but it does not have to be instantiated for its attributes to become part of a Manager object. However, these three attributes, can only be accessed according to the access rules shown in the Five Levels of Accessibility box shown previously. Thus, if the attributes of the Person class are private, which is generally what I prefer, they can only be accessed through the methods in the Person class.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 8

Page 9: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

Figure E.2 – Visualizing a Base Class as Seen “Through” a Derived Class

The following example and accompanying diagram (see Figure E.3) illustrate these points for a small code sample.

Example 4: Inheritance and Access Modifiers – How it all works

public class Person{ . . . private string hiddenName; private int hiddenAge;

// Parameterless constructor public Person() { hiddenName = ""; hiddenAge = 0; } // end parameterless argument constructor

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 9

Derived Class

Public and ProtectedAdditional Data Stores (if any)

These are accessible to any classes derived from this class.

Additional Methods

PrivateHidden Additional Data Stores

Hidden Additional Methods (if any) (Accessible ONLY to this derived class)

Base Class

PrivateHidden Base Data StoresHidden Base Methods

(if any) Not directly accessible to derived class

Public and Protected

Base Class Data Stores (if any)

Base Class Methods(directly accessible to

derived class)

Page 10: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

// Parameterized (Overloaded) constructor public Person (string n, int a){ hiddenName = n; hiddenAge = a; } // end parameterized constructor

public bool commitToDB() { . . .} // not a virtual method

} // end Class

// Subclassing for Specializationpublic class Borrower : Person{ // Specialization - adding a new data store // (not part of Person class) private double hiddenCreditLimit public Borrower() { // Call parameteless Person constructor here. Then … hiddenCreditLimit = 0.0; } // end default constructor public Borrower (string n, int a, double cl): base (n, a) { // First call the parameterized base constructor here // It will allocate memory for and initialize hiddenName // (to n) and hiddenAge (to a) // Then we can allocate memory for and intialize cl. hiddenCreditLimit = cl; } // end overloaded constructor

public bool new commitToDB() { // hides the parent version . . .

} // end commitToDB . . .

} // end Class

Figure E.3 (next page) provides another view of how all of this works, a view that is specific to the current example. It shows that all public and protected members of the base class (in our case, the class Person) are inherited in the derived class (in our case, the Borrower class). These derived class members are therefore visible to any user code in the same way as are the members of the base class. In addition, the derived class may contain its own private, protected, public, etc. members.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 10

Page 11: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

Should there be any naming conflicts between a base class and a derived class, usually caused by hidden or overridable (replaceable) functions such as commitToDB, we can distinguish one member from another using the prefixes base and this. For example,

base.commitToDB() used in the derived class Borrower represents a call the version of commitToDB found in the base class Person. On the other hand

this.commitTODB() or simply commitToDb()

used in the derived class Borrower references the "current instance" of commitToDB (in the Borrower class) which in our example is hiding the version in the Person class.

Figure E.3 (next page) illustrates the most important point about inheritance.

As shown in this Figure, the public properties age and name and the public method

As shown in this Figure, the public properties age and name and the public method commitToDB are all inherited by the Borrower derived class from the Person base class. They are all directly accessible in the Borrower class. Had there been any protected members of the Person class, they too would have been similarly inherited by the Borrower class. HiddenName and HiddenAge are also accessible to the Borrower class, but only through the use of Person class methods allowing this access. Remember that there is a replacement version of commitToDB in the Borrower class and that all calls in Borrower to this method, if not prefixed with base will refer automatically to the replacement method.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 11

A public derived class inherits the data stores of the base class. It also inherits and possibly redefines the methods of the base class. Each object created from a derived type carries with it all the states and behaviors that are defined by its base class. New fields (attributes), properties, and methods may also be added in the derived class. The behaviors of the fields in the base class are not changed by the inheritance. So, it is as though an object of the base class becomes a part of the derived class whenever an object of the derived class is instantiated.

Page 12: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

Figure E.3 – A Derived Class and Its Related Base Class

HiddenName

HiddenAge

creditLimit

E. 5 Encapsulation and Designing with Classes and Inheritance

Encapsulation is the practice of hiding the implementation details of classes and assemblies from other code. Terms such as information hiding,and limiting the need to know are also used when referring to the concept of encapsulation and object-oriented design and programming. Low coupling and high cohesion are also terms we use in discussions of best practices in OOD.

For example, A private member of a class, A, is hidden from ALL other classes in an application A protected member of a class A is hidden from all other classes in an application

other than those derived from A or its descendants An internal member of a class A is hidden from code in other assemblies.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 12

Person Object

Borrower Object Object

name

age

HiddenCreditLimit

toString

commitToDBcommitToDB

Bottom Line – Be as restrictive as possible keeping access to class members carefully guarded. Be sure you have good reasons for loosening access permissions.

Page 13: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

High cohesion – A class should do one thing and only one thing (and do it completely). Put another way, it should have a single responsibility for the management of a single problem domain entity or a single fabricated entity.The latter kind of entity is one that is not obvious from an analysis of the problem domain, but surfaces during an analysis of the desired behavior of a program. (Example: A collection of used numbers in a Bingo Game simulation.)

Low coupling – A class should have least possible dependencies on other code. Connections among classes should be ONLY via the sending of messages from one class to another (or the calling of methods used to pass information to and from a class and the private variables in that class.

Leakage (of information) from one class to another is to be avoided at all costs. Temptations abound leading to leakage. A good example of this may be found in dealing with collections of objects and the objects themselves – candy bars and the list of candy bars in a recent exam. The array list of candy bars should know nothing about the attributes of the list and vice-versa. Not always easy to achieve, but nonetheless essential to a well-constructed project design.

The use of public attributes is to be severely discouraged.

E. 6 Inheritance and Constructors (a bit complicated)

The way in which constructors are handled isn't as obvious. Unless you want to delve into more of the constructor handling complexities, it might be easier to follow a few basic suggestions indicated in this section.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 13

Bottom Line – Be as restrictive as possible keeping access to class members carefully guarded. Be sure you have good reasons for loosening access permissions.

Constructive Constructor Suggestions

1. Always write a no-argument (parameterless) constructor for every class you design. Do this even if you also write some parameterized constructors in the same class.2. Your parameterless constructor can do nothing, but you might wish to use it to ensure that initializations of class variables (even just to blank or null strings, or 0 numeric values, etc.) are handled, even if no other initializations seem to be needed.3. Make sure each constructor in a derived class calls its corresponding base class constructor before it sets about to do its own work. 4. It is true that the compiler will take care of some of these items for you, but it is best not to leave anything to chance.5. It is also a good idea to ensure that all constructors are visible in each class, as opposed to simply generated by the compiler.6. Parameterized constructors in a derived class do not need the same interface as in the base class, but if you are going to pass arguments through to the base class, these arguments must appear in the derived class as well. (See examples in Project IV code.)

Page 14: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

Remember that constructors in an inheritance hierarchy are not inherited. Furthermore, they must be executed in the reverse order. If you do not explicitly call these constructors in the order just described, the compiler will generate these calls for you.

Remember:1. If the derived class has a parameterless constructor, the base class must also

have a parameterless constructor. 2. Each constructor defined in a derived class mustshould explicitly call one of the

accessible constructors in its base class before performing any of its own initialization work.

E.7 Polymorphism and Type Substitution (Replacement)

Inheritance was presented as an effective way to reuse the implementation details of a base class (via construction of derived classes). Another aspect of inheritance lies in its support for polymorphic programming.

Polymorphism

Every derived class inherits the programming contract that is defined by the accessible members of its base class. As a result, you can program against any object created from a derived class by using the same programming contract (that is, the same method with the same argument list and return value) defined by the base class.

In other words …

This polymorphic programming style is a powerful programming technique because it allows you to write generic code. To put it another way, the code, which is written against the base class, is also compatible with any of the derived classes, because they all share a common, inherited design. The derived classes are thus plug-compatible from the perspective of your code, making your code more applicable to a wider range of situations. (Look at some Project IV examples – the save and display methods for example).

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 14

Inheritance provides the ability to program against different types of objects using a single programming contract. This is known as polymorphism (from the Greek meaning "having multiple forms").

Page 15: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

Polymorphism can be envisioned as the notion that an operation (method) supports (may be used on) operands of one or more types (defined using classes).

For example, you can write "X + Y", and the + operation will work in .NET whether X and Y are both integers, floats, decimals (etc.) or strings. Hence the operator "+" is polymorphic, and the code "X + Y" is an example of polymorphic programming.

Example 5: (simple operator overloading – something you already know about)

a.) We could define a fraction class (using integer numerators and denominators) that overloaded the operations +, -, *, /, as well as some basic I/O functions. Then if f1 and f2 were two instantiated objects of that class, the operation

f1 + f2

would make sense. (How would you write the code that carries out this operation using integer operators only?)

b.) The same argument could be made for a set class (How would you define the + operation on sets?)

c.) On the other hand, the + operator defined on a student class might make much less sense and probably would not be defined for such a class. It also makes little sense when defined on the character data type. Try writing the code

a = ‘b’ + ‘c’; in C# (with a of the character data type) and see what you get. Try it in C. It

actually works. Do you know why?

You are already familiar with the idea of overloading as it has been used in many lang-uages. In fact, overloading has been used in higher level programming languages since the mid 1950s, long before the class concept was introduced. The use of overloaded operators in the case of the fraction class illustrates how overloaded operators can be extended using classes to define additional “versions” of the same operator.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 15

Note carefully that in this discussion of simple operator overloading (using the + operator in this case), we assume the existence of many different versions of the same operator, all having the same name (plus) and the same list of arguments. The compiler determines which operator to apply simply by examining the type of the operands involved in an operation.

Page 16: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

Static or Compile-Time Binding

It is important to emphasize the role of the compiler in determining the type of operation (integer, float, fraction, etc) to be performed. In the next example, we examine another situation in which the compiler acts to decide which version of a method to select. These two examples (Examples 5 and 6) involve the use of static (compile-time) binding. But lurking in the wings is an even more interesting situation, dynamic, execution-time binding, where the determination of the method to be used is done during execution, based on the type of the object preceding the “.” in the call of the method.

You have actually seen and probably implemented static method binding when writing multiple constructors for a class. [How did these constructors (all having the same name) differ from one another? And, how did the compiler determine which constructor to call in each case?] In Example 5, we examine a slightly different version of static method binding.

Example 5: Method overloading or static replacement adds yet another dimension to polymorphic programming. This is a bit more complicated, because the compiler now must examine the argument list used in the call in order to determine which version of the method gets called. For example, in the following, the WriteLine method is overloaded:public static class Console { public void WriteLine(); public void WriteLine(string value); public void WriteLine(bool value); ...}

Note carefully that all three of these methods are defined in the same class, a requirement when overloading is used.

In this example, the C#.NET compiler will generate the call to the implementation of WriteLine(string value)when it determines that the caller is passing a string argument (such as "frank" or name). It will call the version of WriteLine (bool value) when it sees a call such as WriteLine(flag); assuming the flag is a type bool variable.

Polymorphism is a very powerful feature of object-oriented programming. It allows you to program in more generic terms and to substitute (replace) different compatible implementations for one another. To design, write, and use class definitions in C# .NET that benefit from polymorphism, however, you must understand a number of additional concepts. The first is the difference between an object type and a reference variable type.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 16

Page 17: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

Example 6: (From an Employee-Manager Project) -- on overloadable methods in which The argument list is used to determine which method is dispatched by a call The argument list involves the use of references to objects that are instances of

classes in a class hierarchy

This example is based on the class hierarchy shown in Figure E.4 (taken from your Final Project Employee-Manager example).

Figure E.4 Product Hierarchy in the Employee-Manager Example.

As shown in Figure E.4 for the EmpMan Project, we have 5 classes. For the leaf node classes (Client, Manager, and Worker) we can create objects and reference those objects in the project. For the Person and Employee classes, we are not allowed to create objects, as they are declared to be abstract. This enables us to model all the entities in our system without necessarily creating objects for each.

As the user of the EmpMan system creates objects of type Client, Manager, or Worker, these objects are added to a list of Persons. This addition of an object to the list of persons might be done as shown next. The list itself is created and hidden in PersonList class using the declaration

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 17

In all polymorphic cases (thus far), the COMPILER determines the version of the method that that is used by examining the operands or arguments used in the call and choosing the version of the method with matching operands. If there is no such version, the compiler will generate an error.

Employee-Manager Type Hierarchy (abbreviated version)

Person

ClientEmployee

WorkerManager

Page 18: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

private List <Person> HiddenPersonList = new List<Person> ();

The code to add an item (Client, Worker, or Manager object) to the list is also in the PersonList class:

// Add a Person to the PersonList public void Add (Person e) { HiddenPersonList.Add(e); } // end Add Worker, Client, or Manager Object to the PersonList

This add method can be called by any of the following three statements.

thisPersonList.Add(thisClient); thisPersonList.Add(thisManager); thisPersonList.Add(thisWorker);

Here we assume there is a declaration

PersonList thisPersonList = new PersonList();

in the same class as the calls to Add.

E.8 Replacing Methods in a Derived ClassDynamic Binding -- How members of a class are accessed at runtime

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 18

The 3 types of objects (Client, Manager, and Worker. are subtypes of the Person type, yet all of them may be added to the List (of Person) declared above. Thus, a list may contain objects of different types as long as the types of these objects are subtypes of the base type (Person in this case) of the list elements. This seems simple enough, but as we shall see in Section E.8, it creates some interesting problems if we want to process (for example, display or save) elements of this list. More on this to come.

It is important to note that, in Example 6, the compiler can determine the object (the list object) involved in the Add operation. (How can the compiler tell?) All cases such as this involve what we call a static resolution (or static binding) of which version of Add gets called.

The 3 types of objects (Client, Manager, and Worker. are subtypes of the Person type, yet all of them may be added to the List (of Person) declared above. Thus a list may contain objects of different types as long as the types of these objects are subtypes of the base type (Person in this case) of the list elements. This seems simple enough, but as we shall see in Section E.8, it creates some interesting problems if we want to process (for example, display or save) elements of this list. More on this to come.

It is important to note here that the compiler can determine the object (the list object) involved in the Add operation. All cases such as this involve what we call a static resolution (or static binding) of which version of Add gets called.

Page 19: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

Static and dynamic binding represent two different ways in which a client can invoke a method or property of an object. Static binding is usually more straightforward and results in better performance. With static binding, as shown above, and as used so far in this course, determinations of which version of a class member (such as a property or method) is called is done at compile time. We have already examined a number of examples of static binding, and we will look at one more here.so we now examine an example in which dynamic binding is used.

Example 7: Suppose we want to display the contents of an item in the Person list.

private List <Person> hiddenPersonList = new List<Person> ();

We could write a method call such as

hiddenPersonList[i].display();

However, recall that the items in the list can be of three different types, Client, Manager, or Worker. Since at least some of the attributes of these three data types are different, each has its own display method. But, looking strictly at the syntax of the above call, we have no way of knowing at compile time the type of the object to be displayed and therefore, no way of determining which display method to call. Worse, the compiler cannot tell what type of object is to be displayed. This is where dynamic binding comes into play.

Dynamic binding yields greater flexibility and is the underlying mechanism that allows a derived class to replace the behavior inherited from a base class at runtime. It is one of the crucial ingredients of polymorphic programming.

Recall that with static binding the type of the reference variable (referencing an object) object that precedes a class member reference is used to decide at compile time which class member (method or property) is being accessed. For example:

solutionObject.compare(guessAreaEntry[i]);

Here the reference variable is solutionObject and its type is known at compile time (type SolutionClass, perhaps). But in the example just shown,

hiddenPersonList[i].display();

the type of the object being referenced by the reference variable is not known to the compiler, but can only be known at execution time when an object reference is actually stored in hiddenPersonList[i]. Only then can the type of the referenced object be determined.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 19

Page 20: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

We now look at another example involving the use of C# lists simply to reinforce what we have said to this point. In this example, as in the previous one, there is no way of determining at compile time the type of object being referenced by any entry of the list. All list entries are defined as references to objects type of type Person. As such, however, they can contain objects of type Person or any of the types derived from Person.

Example 8: The Person Class in the Empman Project contains its own version of the ToString method. The Person version is illustrated below.

public override string ToString() { string s = "ObjectType : " + base.ToString() + "\n"; s += "PersonName : " + hiddenPersonName + "\n"; s += "PersonBirthDate: " + hiddenPersonBirthDate.ToShortDateString() + "\n"; s += "PersonID :" + hiddenPersonID; return s; } // end ToString

This method builds a string by concatenating the name, birthdate, and ID of a Person. This version overrides the base class (Object Class, in this case) version of ToString. In addition, the subclasses of the Person class have their own versions of ToString each of which are referenced as follows with a clause of the form x.ToString(). For example, the version of ToString in the Worker class looks like this:

public override string ToString() { string s = base.ToString() + "\n"; s += "WorkerHourlyPay: " + hiddenWorkerHourlyPay.toString();

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 20

Static binding is the default access (invocation) mechanism used by C# .NET and by the CLR. It is based on the type of the reference variable, not the type of object being referenced used in the reference to a method.

When dynamic binding is used (involving runtime replacements of class members and the keywords override), the determination as to which method is called is made during execution based up the type of the object involved in the access expression.

With static binding, the compiler can perform more of the work. For this reason, static binding can provide a measurable performance advantage over dynamic binding.

Page 21: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

return s; } // end ToStringThe existence of these ToString methods raises a number of issues. But the simple fact that we want to illustrate here has to do with the determination of the version of Tostring that is called.

Question 1: Which version of ToString is called in the line

HiddenWorkerHourlyPay.ToString("C")

ANSWER: The version that is called is determined wholly by the type of the object that precedes the reference (HiddenWorkerHourlyPay in this case). This type is determined by the compiler at compile time. We say that any such determination done at compile time is considered to be a static determination. To state this in another way: we say that the version of ToString that is to be called is bound (determined) at compile time.

Question 2: Consider the following declaration:

private List <Person> HiddenPersonList = new List<Person> ();

In the reference

HiddenPersonList[i].toString() how does the compiler know which version to call?

ANSWER: The compiler does NOT know which version of toString to call (access) because the Person version of toString is defined so as to be overridden by a specific product version (such as Manager or Employer) at runtime. In this case, the determination as to which version of toString is to be accessed has to be made at runtime, after the type of the object referenced by HiddenPersonList[i] has been determined. Thus, if we assigned a Worker object to HiddenPersonList[i] then the Worker class version of ToString would be called. But the assignment of objects to the list is done at run time, so only at run time can we determine the type of the object store in HiddenPersonList[i] . (If no object had yet been assigned at the time of this reference, an exception would be raised.)

Note that this approach to resolving references carries with it considerable "effort" for both the compiler and the runtime system (the CLR). All the type information involved has to be carried along at run time with the data stored in the list. The compiler has to provide all this information, and the CLR has to use it during execution in order to determine the correct version of ToString to use in the above reference.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 21

Page 22: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

Question 3: Is the version of ToString in the Person Class ever used? Why or why not? If not, why bother having it?

ANSWER: The Person version of ToString is used even though there are no Persons per se but only “persons” derived from the Person class.

In an abstract class, any members that we wish to override should be marked with a virtual keyword.

NOTE: If you want to go to extremes and write an abstract class with only definitions and no implementations, you can do this. Such a class is called an Interface in C# .NET) The Person class might be suited to this idea in the EmpMan Project, because we do not intend to ever instantiate any objects of type. Even so, there are some members of the Person class that have useful implementations to be inherited by derived classes, so we chose not to make the Person class an interface.

Dynamic Binding and Overridable Methods (review)

Recall that we claimed earlier in this chapter that one of the most valuable features of inheritance is the fact that the designer of a base class can provide default behavior that can optionally be replaced by a derived class author.

Often, a base class author can provide a default implementation for a method. A derived class author can then choose whether to use the base class implementation or to write a more specialized implementation. In some cases, a derived class author even has the option of extending the base class implementation by adding extra code to the derived class.

Using a derived class to replace the implementation of a method or a property defined in a base class is known as overriding (not to be confused with overloading). Method overriding relies on dynamic binding, not static binding. Overriding a method in a parent class requires that the parent method be written with the modifier virtual in C# (overridable in VB). You will note that both the Save and Display methods in the Person class (or in your OwlMember class) were written using this modifier so that they could be overridden by the Save and Display methods in the subclasses of the Person (OwlMember) class.

Dynamic binding takes into account the type of an object at runtime, which gives the CLR a chance to locate and call the overriding method. In this way dynamic binding supports more flexible code, albeit with a runtime cost.

Dynamic binding is the default in the Java programming language and, in fact, the only option in Java in most cases.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 22

Page 23: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

It is sometimes useful to use a picture to help remember what a list such as hiddenPersonList[i] might look like during execution. Figure E.4 on the next page illustrates what this list might look like, although it is not an expertly drawn picture. Just for fun, we added two other types, a Student type (a derived type from Person) and a Faculty type (a derived type from Employee). Try redrawing the picture in Figure E.1 with these two new types added.

Figure E.4 HiddenPersonList – with 3 items of different types

hiddenPersonList

Note that everything pictured in Figure E.4except the hiddenPersonList Reference belongs in the heap or free store.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 23

[0]

[1] [2]

[3]

[4] [5]

Objet of type Worker

Object of type Manager

Object of typeFaculty

Object of type Manager

Object of type Worker

Object of type Student

Object of type Client

[6]

It is important to note that this type of dynamically (runtime) determined dispatch requires the use of overridable methods with the same name and the same argument list.

Page 24: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

Chaining Calls from a Derived Class to a Base Class

When you override a method, it's fairly common practice to chain a call from your overriding implementation in the derived class to the overridden implementation in the base class. This technique allows you to leverage the implementation provided by the base class and extend it with extra code written in the derived class. Consider the following reimplementation of the Programmer class: public class Worker : Employee { . . . public override void Save(frmEmpMan f) { base.Save(f); HiddenWorkerHourlyPay = Convert.ToDecimal(f.txtWorkerHourlyPay.Text); } // end Save

. . .

The C# .NET keyword base is used in a derived class to explicitly access public or protected members in its base class. In this example, the Worker definition of Save makes an explicit call to the Employee implementation of Save. This approach allows the derived class author to reuse and extend the method implementation provided by the base class author. (Although not shown, note too that the Employee class version of Save in turn references the Person version, extending our chain in this example to three levels.)

Note that chained calls do not have to be made at the beginning of the derived class implementation. They can be made at any point in the overriding implementation.

Example 8: The game of Monopoly. Please click on the word Monopoly to see a full explanation of another example of the benefits of using inheritance with function overrides, chaining, etc.

E.9 SummaryAs you read through this material, one theme probably became clear: The use of inheritance increases your responsibilities both during the design phase and during the coding phase. If you decide to create a class from which other classes will inherit, you must make several extra design decisions.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 24

Page 25: Chapter 5cis-iis1.temple.edu/cis3309/Lecture Set ZZZ Appendix E N…  · Web viewInheritance was made popular more than four decades ago by languages such as C++ and Smalltalk. Since

If you make these decisions incorrectly, you can get yourself into hot water pretty quickly. This is especially true when you need to revise a base class after other programmers have already begun to use it.

The first question you should address is whether it makes sense to use inheritance in your designs. In other words, should you be designing and writing your own base classes to be inherited by other classes? Once you've made a decision to use inheritance in this manner, you take on all the responsibilities of a base class author. It's your job to make sure that every derived class author understands the programming contract your base class has defined for its derived classes.

While you may never create your own base class, you will more than likely create custom classes that inherit from someone else's base class. It's difficult to imagine that you could program against the classes of the FCL without being required to inherit from a system-provided base class such as the Form class, the Page class, or the WebService class.

Because so many of the class libraries in the .NET Framework have designs thatinvolve base classes, every .NET developer needs to understand the responsibilities of a derived class author. If you don't have a solid understanding of the issues at hand, you will find it difficult to implement a derived class correctly.

Appendix E – Inheritance 5/20/2023 4:01:19 PM Page 25