C# book by Alfred Thompson

Chapter 9

Inheritance and Interfaces

Student Objectives

Introduction

Inheritance allows a program to create new classes that include well-tested and debugged existing classes. This existing class, called a base class, has properties and methods that will automatically be part of this new or derived class. The derived class may also include additional properties and methods to add functionality to the new class.

Methods in the base class can also be overloaded in the derived class. This allows a programmer to use the same methods with similar classes but get results that are appropriate for the class being used.

Base Classes

A base class is a class that will be used to build other classes. Classes are usually thought of as a hierarchy. This means that each class is considered to be "of a" type that it is created from. This is a common situation in the physical world. Consider how we think of cars. A sports car is of they general type of car. A car is of the type of personal or family vehicle. Personal vehicle are of the type vehicle. Other classes that are derived from vehicle include trucks and busses each of which have classes that are derived from them. The base vehicle has wheels and a propulsion system but the implementation is not specified. In other words, each derived vehicle determines how many wheels it has and how it is driven. Cars have four wheels and generally are gasoline driven. There are classes of cars that use other power sources such as electricity or hybrid power. Those would be in separate classes. Trucks may have 4 wheels, 6 wheels, 18 wheels or other numbers. All of these vehicles have common functions and can be treated the same for some purposes.

While any class can be used as a base class, some classes are designed especially to serve as base classes. A good base class has basic functionality that can be used by any class that will be of that type. A derived class will be able to use those methods and properties and not reinvent them. This will allows the programmer to concentrate on the properties and methods that are unique to the derived class.

The class below is called store and will be used for a number of examples in this chapter.

 

public class store

{

private string name;    // Store name

protected decimal sales;// Annual sales

protected int size;           // Size of store in square feet

protected int employees;// Number of store employees

 

// Store constructor - accepts a store name

public store (string name)

{

      this.name = name;

}

// This method returns a store efficiency value that can

// be used to compare the results from similar stores

public decimal Efficiency()

{

      // Calculate sales per square foot

      return (sales/size);

}

public string Name

{

      set

      {

            name = value;

      }

      get

      {

            return name;

      }

}

public int Size

{

      set

      {

            if (value > 0)

            {

                  size = value;

            }

            else

            {

                 

            throw (new Exception ("Size must be a positive value"));

            }

      }

      get

      {

            return size;

      }

}

public decimal Sales

{

      set

      {

            sales = value;

      }

      get

      {

            return sales;

      }

}

public int Employees

{

      set

      {

            employees = value;

      }

      get

      {

            return employees;

      }

}

}

This class includes a number of properties with their associated get and set methods. You should notice that the data fields are declared as protected and not public or private. Protected variables may be accessed inside the base class or inside any class that is derived from the base class. Private variables may not be accessed inside derived classes. That would make the derived class harder to use then is desirable. Public variables would be accessible from any class even those that are not derived from the base class. That access violates the principle of encapsulation and is undesirable. Protected is the compromise that is used to make inheritance more efficient.

Creating a Derived Class

A store class is useful for thinking about stores in general but is not adequate for every purpose. For example, retail stores and wholesale stores sometimes use different measures of productivity. Retail stores often judge the efficiency of a store by calculating the sales per square foot. The store class above uses that very formula for its Efficiency method.

You can create a new derived class to handle retail stores very simply because all the properties and methods are the same as those of the base store class. The derived class may also be referred to as an example of the base class. in other words, a retailStore is a store. It is just a specific kind of store. This "is a" relationship is important to understand as it implies that the derived class can be treated in much the same way as its base class.

A derived class is created the same way as an ordinary class with the addition of the name of the class to inherit added to the end of the class statement after a colon. The code below creates a new class that inherits the store class.

class retailStore : store

      {

            public retailStore (string name) : base(name)

            {

                 

            }

      }

A new constructor must be created for this class. This constructor will set the name value the same way the base class constructor. Rather then write new code for the constructor, derived classes can call the base class constructor in an initialization list. The class declared above can be used to declare variables that can use the methods and properties in the base class. The sample code below creates an object of type retailStore, sets the values of several properties and then calls the Efficiency method to display the stores productivity value.

retailStore store1 = new retailStore("Acme Supply");

store1.Employees = 5;

store1.Sales = 10324000.00M;

store1.Size = 10000;

Console.WriteLine("Efficiency of {0} is {1:C} per square foot", store1.Name,store1.Efficiency());

 

What has been created so far is a second class with a new name that is otherwise identical to the base class. This can be helpful in some cases but does not show the power of inheritance.

A wholesale store is somewhat different from a retail store. Wholesale stores sell in bulk and that means they must have a lot of space. For sake of example, let us assume that they use a different measure of efficiency or productivity. Our wholesaleStore class will use sales divided by number of employees in the Efficiency method. The same method name should be used to calculate efficiency for all stores so the new class will overload the Efficiency method. The code below creates a wholesaleStore derived from the store class.

class wholesaleStore : store

{

      public wholesaleStore(string name) : base(name)

      {

                 

      }

      public wholesaleStore(string name, decimal sales, int size,

int employees) : base(name)

      {

            this.sales = sales;

            this.size = size;

            this.employees = employees;

      }

      public new decimal Efficiency()

      {

            return (sales/employees);

      }

}

 

The keyword new is used in the Efficiency method declaration to indicate that this is a new method that is used in place of the base class's version.

This new class also implements two constructors. A simple constructor that just sets the store name and a more complex constructor that sets all the data values in the class. The base constructor is used to set the name value in both constructors. The second constructor uses the reserved word this to differentiate the variables that are part of the class from the variables that hold the data passed to the constructor. If different names were used to pass values into the method then the this keyword word could be avoided.

The following code creates two objects of type wholesaleStore and then displays the Efficiency value for the stores.

wholesaleStore bigStore = new wholesaleStore("Acme Warehouse");

bigStore.Employees = 5;

bigStore.Sales = 10324000.00M;

bigStore.Size     = 10000;

Console.WriteLine("Efficiency of {0} is {1:C} per employee", bigStore.Name,bigStore.Efficiency());

 

wholesaleStore hugeStore = new wholesaleStore("Big Stuff R Us", 4500000m,100000,10);

Console.WriteLine("Efficiency of {0} is {1:C} per employee", hugeStore.Name,hugeStore.Efficiency());

 

These new objects will use the Efficiency method for wholesaleStore class objects and not the base class method.

Objects that are derived from a class can be cast into the base class. For example, the following code

Objects of a derived class can be used with arrays of the base class. That is because a retailStore or a wholesaleStore are both still stores. They have an "is a" relationship. The following code copies three objects, two wholesaleClass objects and one retailStore object, into an array of stores.

      store [] allStores = new store[3];

      allStores[0] = bigStore;

      allStores[1] = store1;

      allStores[2] = hugeStore;

      foreach (store s in allStores)

      {

       Console.WriteLine("{0} has {1} employees", s.Name, s.Employees);

      }

The code above works without any problems because both classes use the same property definitions for Name and Employee as the base class. The Efficiency method is defined differently for retailStore and wholesaleStore. The following code will use the base method for all stores. This will not give the desired result.

foreach (store s in allStores)

{

Console.WriteLine("The efficiency rating for store {0} is {1:C}", s.Name, s.Efficiency());     

}

The ideal case would be for the system to understand which objects were of which type and use the correct version of the Efficiency method. A few changes can make that possible. Virtual methods are defined in a base class with the use of the keyword virtual. The following code defines the Efficiency method for the store base class.

            virtual public decimal Efficiency()

            {

                  return (sales/size);

            }

Virtual methods are overridden in the derived class using the override keyword as in the following code example.

            public override decimal Efficiency()

            {

                  return (sales/employees);

            }

The following code, which looks the same as that used earlier, will now use the correct version of Efficiency for the derived classes used.

      store [] allStores = new store[3];

      allStores[0] = bigStore;

      allStores[1] = store1;

      allStores[2] = hugeStore;

      foreach (store s in allStores)

      {

       Console.WriteLine("{0} has {1} employees", s.Name, s.Employees);

      }

The system will determine which derived class an object belongs in and use the appropriate overloaded version of Efficiency.

Some classes are only useful as base classes. In other words, you can create a class that is a base class but that cannot be used to create objects. These are called abstract classes. These classes include only abstract methods which must be overloaded by the derived class. The following code declares an abstract class.

public abstract class person

{

 . . .

 public abstract int SetAge(int myAge);

 . . .

}

 

There are times when a class should not be used as a base class. C# provides the sealed keyword to identify these classes.  Sealed classes, which cannot be inherited, are the logical opposite of abstract classes which must be inherited. The following snippet of code declares a sealed class.

public sealed class pele

{

     . . .

}

Interfaces

An interface is a reference type, similar to a class, which includes only methods. These methods are declared by the interface but are not implemented by the interface. A class that includes an interface must implement all the methods in the interface.

Interfaces are useful because they allow methods to process any class that implements a specific interface. A method that is expecting a class as a parameter will support only that class and classes that are derived from it. Other classes cannot easily be passed into that method. Because a class can only inherit one class but can inherit multiple interfaces, using an interface gives a programmer much more flexibility. Interfaces are designed for common sets of methods that are useful across a variety of classes. Interfaces are especially helpful for classes that have very little in common except for a set of common operations.

An interface is declared using the interface keyword followed by the name of the interface. The interface name in the .NET Common Language Library all start with a capital I and many programmers follow that convention. This helps identify interfaces and prevent confusion with classes. The methods in an interface are identified but not implemented. These methods will be implemented in the classes that inherit the interface. A sample interface definition follows.

      interface ICool

      {

            void moreCool(float increase);

            void lessCool(float decrease);

            int coolValue();

      }

This interface defines three methods, all of which must be implemented by any class that inherits the ICool interface. Each class would define the values needed to track cool, change cool, and return cool. Any method that accepted ICool as a parameter could use these values without needing to know the actual implementation in a specific class.

It is probably helpful to see this in action using an existing and powerful interface. The Array class has a number of useful methods including BinarySearch and Sort that use the IComparable interface. This interface defines a method called CompareTo that is used to compare items in the class. In order for the Array class to sort or use a binary search to search an array of a class, the class must inherit IComparable and implement CompareTo. The Array class methods would have no way of knowing how to arrange elements of the array without this interface.

A class may have many data elements of different types. The Array class is dependent on the implementation of CompareTo to determine which field or fields to use and how they should be used. The CompareTo method returns an integer value that indicates if two items being compared have the same value or if one is greater then the other. A zero is returned if both items have the same value. If the first item is greater in value to the second then a one is returned. A minus one is returned if the second item is greater in value to the first.

The following code examples inherit the IComparable interface and implement the CompareTo method. The start of the class definition has the IComparable name added as being inherited.

      public class store : IComparable

The CompareTo method must then be implemented inside the class.

      int IComparable.CompareTo(object obj)

            {

                  store store2 = (store) obj;

                  if (this.Sales == store2.Sales)

                  {

                        return 0;

                  }

                  else

                        if (this.Sales < store2.Sales)

                  {

                        return -1;

                  }

                  else return 1;

            }

The methods from interfaces are always public and that does not have to be specified. This implementation uses the sales property as the sort key. IComparable only allows one sort key for a class.

Now that the IComparable interface is inherited, the Sort and BinarySearch methods of the Array class can be used with the store class. The following code will add a number of stores to an array, sort the array and print store information in order by sales.

      store [] allStores = new store[3];

      allStores[0] = bigStore;

      allStores[1] = store1;

      allStores[2] = hugeStore;

      Array.Sort(allStores);

      foreach (store s in allStores)

      {

Console.WriteLine("{0} \t has sales of {1:c}", s.Name, s.Sales);

      }

Classes that will be sorted and searched in arrays are prime candidates for inheriting the IComparable interface.

Summary

The ability to create new classed based on existing classes is one of the things that makes object-oriented programming more powerful then procedural programming. A derived class inherits the data, properties and methods of its base class. The derived class can add its own data and methods to give it increased power and flexibility.

The derived class may overload methods in the base class by listing them as new. The new method will be used for objected of the derived class but will not be used if the derived class is cast into the base class. Virtual methods in a base class are overloaded in derived classes using the overload keyword. When a method is an overload of a virtual method the system will use the overloaded method even when an object is cast into the base class.

Abstract methods are method declarations, without implementation, in a base class. Abstract methods must always be implemented by a derived class. Abstract classes are classes that can be used as base classes but which cannot be used to create objects in their own right. All methods in an abstract class are implemented in a derived class. Sealed classes are the opposite of abstract classes. They can be used to create new object but cannot serve as a base class.

Review Questions

  1. What type of method must be defined in a base class for the system to identify overloaded versions in derived classes?
  2. What keyword is used to identify a method that replaces a method in a derived class?
  3. What keyword must be used to declare class data that may be directly referenced in a derived class?
  4. How many of the methods in an interface must a class that implements it define?
  5. How can the base class constructor be used by the constructor for a derived class?
  6. How many classes can be inherited by a derived class?
  7. How many interfaces can be inhered by a derived class?
  8. Describe an "is a" relationship.
  9. What interface must be inherited by a class so that the class can be sorted using the Array.Reverse method?
  10. What key word will prevent a class from serving as a base class?


 

Vocabulary

Base Class   A class that serves as the foundation for creating a new class

Derived Class          A new class that inherits properties and methods from a previously existing class.

Interface        A reference type, similar to a class, that includes only methods that must be implemented by classes that include the interface.

Protected      Protected methods and variables may be accessed directly by classes that are derived from the class that defines them. Other classes, not derived from that class, may not directly access protected methods or variables.


 

Projects