Skip to content
wannacode

Interfaces

Problem

Salaries and invoices

At the end of the month the company pays two kinds of things: the employees' salaries and the suppliers' invoices. You want one array and one loop: who gets paid, how much, and the total at the end.

An Employee[] array will not work: an invoice is not an employee. Making it inherit from Employee is wrong. The sentence "an invoice is an employee" is not true, and the invoice would also get a name, a salary and a raise.

A common parent Payment will not work either. Manager already has a parent, Employee, and a class can have only one. Employees and invoices have nothing in common except that they can be paid.

The idea

An interface: a "can do" contract

An interface is a list of methods without bodies. It says what a class must be able to do, but not how:

interface IPayable
{
int AmountDue();
string Payee();
}
A class that accepts the contract implements the interface and writes each of its methods:
class Invoice : IPayable
{
public string Supplier { get; private set; }
public int Amount { get; private set; }
public Invoice(string supplier, int amount)
{
Supplier = supplier;
Amount = amount;
}
public int AmountDue()
{
return Amount;
}
public string Payee()
{
return Supplier;
}
}
Invoice power = new Invoice("Power company", 340);
Console.WriteLine(power.Payee() + ": €" + power.AmountDue());
Output
Power company: €340

You implement an interface with the same colon as inheritance. You write the methods without override: there is nothing to override, because the interface has no versions of its own.

In C# interface names start with I: IPayable, IComparable, IDisposable. That way class Invoice : IPayable shows right away that the colon is for an interface.

Interface methods are always public. They are a promise to everyone, so in the class they have to be public too.

See it

One loop for everything that gets paid

The employee accepts the contract too. Its amount is the monthly salary:

class Employee : IPayable
{
// ... Name, Salary, the constructor, MonthlyPay()
public int AmountDue()
{
return MonthlyPay();
}
public string Payee()
{
return Name;
}
}

Now both the employees and the invoices are IPayable. An interface is a type: it can be the type of a variable, a parameter or an array.

IPayable[] payments =
{
new Employee("Ivan", 2000),
new Manager("Maria", 3000, 500),
new Invoice("Power company", 340),
new Invoice("Landlord", 1800)
};
int total = 0;
foreach (IPayable p in payments)
{
Console.WriteLine(p.Payee() + ": €" + p.AmountDue());
total += p.AmountDue();
}
Console.WriteLine("Total: €" + total);
Output
Ivan: €2000
Maria: €3500
Power company: €340
Landlord: €1800
Total: €7640

The manager writes nothing new. From Employee it inherits both the contract and AmountDue(). And AmountDue() calls MonthlyPay(), which the manager has overridden. That is why Maria gets €3500.

IPayable[] payments =
{
new Employee("Ivan", 2000),
new Manager("Maria", 3000, 500),
new Invoice("Power company", 340)
};
int total = 0;
foreach (IPayable p in payments)
{
total += p.AmountDue();
}
Console.WriteLine("Total: €" + total);
Step by step, in words
  1. The array is of type IPayable. The arrows point to three classes, but they reach only their contract.
  2. total starts at 0.
  3. p points to the employee: AmountDue() returns the salary, 2000.
  4. The manager is an employee, so the manager can be paid too. Salary plus bonus: 3500.
  5. The invoice is not an employee, but it keeps the same contract: 340.
  6. The loop does not know the classes. It only knows that each one can be paid.

Try it

What is the amount?

What will the program print? The classes are from the card above.

IPayable p = new Manager("Maria", 3000, 500);
Console.WriteLine(p.AmountDue());
Answer

The idea

One parent, many contracts

Accounting also wants the invoices in CSV format: one "supplier;amount" line each. That is a second contract. A class can implement as many interfaces as it wants, separated by commas:

interface IExportable
{
string ToCsv();
}
class Invoice : IPayable, IExportable
{
// ... as before
public string ToCsv()
{
return Supplier + ";" + Amount;
}
}
IExportable[] forAccounting = { new Invoice("Power company", 340), new Invoice("Landlord", 1800) };
foreach (IExportable e in forAccounting)
{
Console.WriteLine(e.ToCsv());
}
Output
Power company;340
Landlord;1800

If the class has both a parent and interfaces, the parent comes first:

class Manager : Employee, IExportable

The first name after the colon can be a class. All the others are interfaces.

There is one parent and there are many contracts. That way a class whose parent is already taken can still go into a common array with completely different classes.

Try it

Find the bug

This invoice does not compile. Which line has the bug?

Pick the line with the bug.

Try it

What is missing?

class Invoice  IPayable
{
    // ...
}

The idea

Abstract class or interface?

Both are parents you cannot create objects from, and both put obligations on the classes below them. The difference is in what they say:

Abstract class Interface
Says "X is a Y" "X can"
How many per class one as many as you want
Fields and constructor yes no
Shared code yes hardly any
  • An abstract class, when the classes below it are one kind of thing and share code and data: shapes have a name and a description, employees have a name and a salary.
  • An interface, when the classes are different but need a common ability: an employee and an invoice "can be paid", a bird and a plane "can fly".
They are often used together: Employee is the parent of the manager and the intern, and at the same time it implements IPayable.
More precisely, if you are curious
  • Since C# 8 an interface can have a default method, with a body. Classes get it ready-made, and they can also write their own. That way you can add a new method to an interface without breaking the classes that already implement it. You call such a method through a variable of the interface type (IFlyable f), not through Bird:
interface IFlyable
{
void Fly();
void Glide()
{
Console.WriteLine("Gliding through the air");
}
}
class Bird : IFlyable
{
public void Fly()
{
Console.WriteLine("The bird flaps its wings");
}
}
class Plane : IFlyable
{
public void Fly()
{
Console.WriteLine("The plane takes off");
}
}
IFlyable[] flyers = { new Bird(), new Plane() };
foreach (IFlyable f in flyers)
{
f.Fly();
f.Glide();
}
Output
The bird flaps its wings
Gliding through the air
The plane takes off
Gliding through the air
  • An interface cannot have fields. It can have properties, but only as a promise: int Amount { get; } says "every class will have this property".

Exercise

Contracts

Solve the tasks in your editor.

1. Horn. An interface IHonkable with a method Honk(). SimpleCar from the module "Classes and objects" implements it.

Output
Beep beep!

2. Details. An interface IPrintable with a method PrintDetails(). Implement it in Person, and have Student (a child of Person) override it and add the grade. Put both of them in an IPrintable[] array.

Output
Name: Georgi, age: 45
Name: Elena, age: 19
Average grade: 5.5

3. Dance. An interface IDanceable with a method Dance(). DanceMove (a name and a duration) implements it.

Output
Dancing salsa for 30 seconds

4. DJ. A class Song with a title. An interface IPlayable with a method Play(). DJ implements it and has a method PlaySong(Song song) that remembers the song and plays it. If there is no song yet, Play() says so.

Output
No song selected
Playing 'Bohemian Rhapsody'

5. The pond. Interfaces ISwimmable with Swim() and IFlyable with Fly(). Fish swims. Duck both swims and flies.

Output
The fish swims.
The duck swims.
The duck flies.

More tasks

  • Discount. An interface IDiscountable with a method that returns the price after the discount. Implement it in Product (10% off) and in Subscription (25% off for students). Work out the total of a cart with a €50 product and a €12 student subscription: €45 + €9 = €54.
  • CSV for everyone. Make Employee implement IExportable too, with a "name;monthly salary" line. Put employees and invoices in one IExportable[] array.

Test

Check yourself

1. How many interfaces can one class implement?
2. Does new IPayable() compile?
3. A class implements an interface method without public. What happens?
4. Do you write override before a method that implements an interface?
5. Employees and invoices have to be paid in one loop. They share no code. What fits better?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·