developmentor - a developer services company				  
				      C# Tutorial
   Modules

Inheritance


Goals:

  • Introduce the syntax and semantics of inheritance.
  • Discuss the protected access level.
  • Show how to call base class methods and constructors.

Overview

Inheritance allows a 'derived' class to be created from an existing 'base' class. The new class inherits the fields and methods of the base class. In addition, the new class can add new methods, add new fields, and override existing methods.


Motivation

Inheritance provides a way to model the real world concept of generalization/specialization. Consider the different types of people at a university described in the following diagram.

The types at the top of the hierarchy are very general since everyone at the university is a person. Proceeding down the hierarchy things become more specialized because some of the people at the university are students and some are employees. Students and employees have some behavior in common but differ in other ways. The parts they share are captured in the base person class while their differences appear in the derived student and employee classes. Similar comments apply lower in the hierarchy since some of the students at the university are undergraduates and some are graduates. The common term for this type of generalization/specialization is the "is-a" relationship. One might say "a student is a person" or "a faculty member is a employee".

The example above intentionally uses types of people in order to demonstrate that the concept of the is-a relationship is not exclusive to programming. Most humans naturally create mental categories such as person, fruit, employee, shape, mammal, etc. to help them deal with the complexity of the real world. The categories capture the elements that all members of the group share. For example, all employees might have an id number and make a certain salary, all shapes might have a color and take up a certain area when drawn on a piece of paper, and so on. This organization makes the world a much simpler place because an understanding of a category gives a lot of information about the behavior of each member. In other words, if you understand a basic concept such as apple, it will help you when you encounter a new member of the group such as Fuji or Braeburn for the first time.

Inheritance gives developers a way to express the is-a relationship in their code. This is especially important in object-oriented programming where the goal is to model the real world accurately. If in the real world, the people at a university are categorized as shown in the diagram above, then a software model of the university should be sure to capture those relationships. The common wisdom then, says that whenever the is-a relationship exists in the application domain, it should be modeled in software using inheritance.

At this point, the discussion of the meaning and benefits of inheritance has been mostly theoretical. It might help to look at a more concrete advantage as well: elimination of repeated code. Consider the two classes below and note how several lines of identical code appears in both.

The disadvantages of repeated code are well known: extra work to program and extra work to debug and maintain. In the following discussion, we will see how to rewrite the above example so the repeated code is factored out into a base class called Person. The Student and Employee classes will be derived from Person to obtain the functionality they need without having to repeat the implementation of the shared code.


Basic syntax

A class specifies its base class by placing a colon and the base class name as part of its declaration. The example code below shows a base class Person and two derived classed Student and Employee. Note how the fields and methods that apply to both students and employees appear in the base class while the specific members are placed in the derived classes.

An inheritance hierarchy can be as deep as required. For example, a Faculty class could be derived from Employee which, in turn, is derived from Person. The Faculty class inherits the members of all ancestor classes; that is, it inherits from Employee and Person.


Memory allocation

Creating a derived class object allocates memory for the fields declared in both the base class and the derived class.


Base services

Public members of the base class are available to clients of the derived class. That is, client code that is using an instance of the derived class can access any public fields or call any public methods that are declared in the base class.


Single inheritance

C# offers only single inheritance which means that a class can have only one direct base class. This restriction is actually imposed by the CLR so single inheritance is the rule for all .NET languages. The designers of .NET acknowledge that multiple inheritance is more powerful than single inheritance; however, they decided that the extra complications and ambiguous situations that arise in the presence of multiple base classes was not worth this extra power.


Protected access

There is a third access level for class members in addition to the more common public and private. The protected access level grants access to derived classes but prevents access from all other client code.


Hiding inherited member

It is possible that a base class and a derived class might contain an identical member. The example below shows the issue.

This preceding code has two ambiguities. The base class and derived class have an accessible field with the same name and they contain a method with the same signature (name and parameter list). To see how such situations might arise in practice, let's revisit the Person and Student classes and consider how the development process might have taken place over time.

Suppose the person class was created first. In most countries, each person is given an id number by the government so it would make sense for person to have an id field and one or more access methods as shown below.

Once the basic person class is complete, another team member might begin writing a derived class such as Student.

At this point in the development process there are no ambiguities. Because everything is working correctly, the designers of Person and Student decide to release their classes to the rest of the development team. The other team members are happy because they have been waiting for these classes in order to move their own parts of the project forward. Client code such as the following now begins to be written.

Now suppose the Student class designer realizes that most universities assign each student a unique id number. They return to the class and add an id field and one or more access methods as shown below.

The addition of the SetId method to the Student class has resulted in an ambiguity since Student now has two methods with the same signature. Consider the effect on the client code: which method should be called now, the Person version or the Student version?

It turns out that the derived class method takes precedence over the base class method. Therefore, the behavior of the client code will change, where previously the Person version of SetId was invoked now the Student version will be called.

The C# design team considered this type of behavior change too important to ignore; therefore, ambiguities between base and derived classes generate a warning from the compiler. When the designer of the Student class rebuilds the code, they will see a warning and be aware that the addition of the SetId method will change the behavior of client code. At this point, the Student class designer has a couple of choices. Arguably, the best decision would be to change the name of the Student version of the SetId method to something that doesn't conflict with the inherited method. If this is not an option, they must add the keyword new to the definition of the student SetId method to acknowledge that the new method will hide the inherited one. Adding new to the method definition will suppress the compiler warning. Note that the behavior of the client code did still change, and the Student class designer should probably make an effort to notify all the clients of the change. The point is that the change did not occur silently.

The keyword new may also need to be applied to fields whenever a derived class field conflicts with a field inherited from its base class. In the Person and Student example, new is not needed on the id field of Student even though Person defines a field with the same name. This is because the person id field is private and so is not accessible to Student and therefore there is no ambiguity.

In practice, inheritance hierarchies created by a single team will likely be carefully designed to minimize these types of ambiguities so the use of new should be rare.


Invoking base class methods

A derived class often needs to invoke a method defined in its base class. Generally, this is extremely easy and the inherited method can be called just as if it were a method of the derived class. The code below shows this simple case.

If the base and derived class both have a method with the same signature, the derived class must use the keyword base to get access to the base class version. If the keyword base were omitted from the call, the derived class version would be invoked instead of the base class version.


Construction and inheritance

It is common for a base class to require initialization. For example, the following class would likely need to initialize the person's name and age.

The base class would probably offer one or more constructors to do the required initialization.

Derived classes inherit all the fields of the base class and add their own. When a derived class object is created, both the derived class part of the object and the base class part must be initialized.

The derived class constructor can invoke a base class constructor to initialize the base class part of the object using the :base(...) syntax. The constructor arguments are placed inside the parentheses as in a regular method call and the entire construct goes after the method signature but before the open brace for the constructor body. At runtime, the base class constructor will be executed first followed by the body of the derived class constructor. The official name for this technique is a "constructor initializer".

This material is excerpted from the Programming C# course offered by DevelopMentor.