Interfaces and Polymorphism

Suppose your company started using independent contractors.  Contractors (technically) are not employees, so
you do not want to derive a new class from Employee.  Furthermore, contractors are demanding creatures, so
they prefer a method called PayMe instead of ComputePay.  Is there a way to include contractors without
changing the code in ComputePayroll?  Here is the class diagram:



























By defining an
interface, you can link the entire Employee hierarchy and the Contractor class.  An interface
defines a common set of behaviors.  They are useful whenever classes share related behaviors but otherwise do
not exhibit an Is-A relationship.  Remember, inheritance models Is-A relationships.  Here is the class diagram
with the IPayable interface mixed in:






























You declare interfaces using the
interface keyword.  By convention, interfaces are named with an “I” prefix.  
Interfaces can contain methods and properties but not fields.  All interface members are implicitly abstract and
public.


public interface IPayable
{
  double ComputePay();
}


Use the colon operator to declare that a class supports an interface (like inheritance).  In doing so, you guarantee
that
every member of the interface is implemented.


// Compile Error! Contractor must implement IPayable.ComputePay.

class Contractor : IPayable
{
}


To implement the interface, declare a public method that matches the signature of the interface method:


class Contractor : IPayable
{

  // Implements IPayable.ComputePay()

  public double ComputePay()
  { // ...
  }
}


Abstract class members also qualify as interface implementation.  Of course, derived classes must override
abstract members. Thus, all derived classes implement the interface.  Therefore, the following code specifies that
all Employee subclasses implement the IPayable interface.


public abstract class Employee : IPayable
{

  // This implements IPayable.ComputePay. Given that all derived
  // classes (WageEmployee, SalesEmployee, Manager) implement
  // ComputePay, they also implement the IPayable interface.
 
  abstract public double ComputePay();
...
}


Explicit Interface Implementation

Remember, the Contractor class exposes a PayMe method instead of ComputePay as required by the IPayable
interface.  You can work around this by implementing the ComputePay method to forward the call to PayMe.


public class Contractor : IPayable
{

  // Implements IPayable.ComputePay().

  public double ComputePay()
  {

     // Forward call to the PayMe method.

     return PayMe();
  }

  public double PayMe()
  { return 3000.00; }   
}


However, this exposes two public methods that do exactly the same thing.


static void Main(string[] args)
{
  Contractor contractor = new Contractor();

  // Both of these methods do the same thing!

  contractor.PayMe();
  contractor.ComputePay();
}


In this case, the Contractor class should use explicit interface implementation.  You explicitly implement an
interface member by qualifying the name with the interface name.   You cannot provide an access modifier. It is
always implicitly private.


public class Contractor : IPayable
{

  // Explicitly implements IPayable.ComputePay().
  // Note: No access modifier.

  double IPayable.ComputePay()
  {
     // Forward call to the PayMe method.
     return PayMe();
  }

  public double PayMe()
  { return 3000.00; }   
}


An explicitly implemented interface member can only be invoked on a reference typed as the interface.  Because
it is private, it cannot be invoked on a reference typed as the class.


static void Main(string[] args)
{
  Contractor contractor = new Contractor();

  // Call PayMe on class reference. OK!

  contractor.PayMe();

  // Compile Error! Trying to access a private member!

  contractor.ComputePay();
}


To invoke ComputePay, you must use an IPayable reference or cast the Contractor reference to the IPayable
interface.


static void Main(string[] args)
{
  Contractor contractor = new Contractor();

  // Call PayMe on class reference.

  contractor.PayMe();
  

  // Use interface reference to call ComputePay.

  IPayable payee = contractor;
  payee.ComputePay();

  // Or cast contractor to IPayable interface.

  ((IPayable)contractor).ComputePay();
}


Now you can update the ComputePayroll method.  You just need to change the input parameter to accept an
array of objects that implement the IPayable interface.  Therefore, ComputePayroll can now accept any type of
Employee and Contractors.


static double ComputePayroll(IPayable[] staff)
{
  double result = 0;
  for(int i = 0; i < staff.Length; i++)
  {
     result += staff[i].ComputePay();
  }
  return result;
}


The following code tests the ComputePayroll method:


static void Main(string[] args)
{
  Contractor contractor = new Contractor();
...

  // Must call PayMe on class reference.

  Console.WriteLine("Contractor: {0}", contractor.PayMe());

  IPayable[] staff = new IPayable[4];
  staff[0] = wage;
  staff[1] = sale;
  staff[2] = manager;
  staff[3] = contractor;

  Console.WriteLine("Payroll:    {0}", ComputePayroll(staff));
  Console.ReadLine();
}














Interfaces versus Abstract Classes

Another way to solve the previous contractor problem would be to replace the IPayable interface with an
abstract Payee class.  The Payee class is more generic than Employee and Contractor and represents anyone the
company pays.  The advantage of this is that it allows you to provide a default implementation that derived
classes can use as is or override.


public abstract class Payee
{
  public virtual double ComputePay()
  {

     // Provide a default implementation that derived classes
     // can use or override.

     return 3000;
  }
}

public abstract class Employee : Payee
{
...
}

public class Contractor : Payee
{
...
}


The disadvantage to this approach relates to the fact that .NET does not support multiple inheritance.  
Therefore, if you derive from Payee to get the ComputePay behavior, you cannot derive the class from anything
else.  For example, assume you have an Intern class that is both a Student and a Payee:


public abstract class Student
{
 // Basic student stuff ...
}

// An Intern IS A Student and IS A Payee. However, .NET does not
// support MI for classes. Compile Error!

public class Intern : Student, Payee
{
...
}


The advantage of the interface approach is that a class can implement multiple interfaces.  A class can also
derive from a base class and implement multiple interfaces. In this case, the base class must come first in the list
following the colon.


public abstract class Student
{
 // Basic student stuff ...
}

// An Intern IS A Student and implements multiple interfaces
// including IPayable.

public class Intern : Student, IPayable, IAnotherInterface, IEtc
{
 // Implement the interfaces ...
}


If the implemented interfaces contain identical members, then you can use explicit interface implementation to
distinguish them.  Assume IAnotherInterface also has a ComputePay method. In this example, the one
ComputePay method implements both interfaces:


public interface IAnotherInterface
{
  double ComputePay();
}

public class Intern : Student, IPayable, IAnotherInterface, IEtc
{  
  // Implement the interfaces ...

  // Implements BOTH IPayable and IAnotherInterface

  public double ComputePay()
  {return 300;}
}


If Intern requires a different implementation for each interface, then use explicit interface implementation:


public class Intern : Student, IPayable, IAnotherInterface, IEtc
{  // Implement the interfaces ...

  // Implements IPayable

  double IPayable.ComputePay()
  {return 500;}

  // Implements IAnotherInterface

  double IAnotherInterface.ComputePay()
  { return 300;}
}

























































































































Interfaces and Polymorphism
Table of Contents
C# Tutorial | C#.NET Tutorial | Interfaces Tutorial

Copyright (c) 2008.  Intertech, Inc. All Rights Reserved.  This information is to be used
exclusively as an online learning aid.  Any attempts to copy, reproduce, or use for training is
strictly prohibited.
Courseware
Training Resources
Tutorials
Services