4 min read

Interfaces

Crash Course

Introduction

When talking about inheritance, I stated that inheritance is a rather strong association between two types. Sometimes, two types share a similar role or perform the same job in unique ways, where the types are not meant to be as closely tied together as inheritance requires.

In fact, the example we saw in the previous tutorial is very much in this vein: a human player and a computer player have no direct relationship to each other, aside from the fact that they can both play the game by providing a move to make on demand.

As an alternative to a base class an inheritance, it is possible to simply define the job or responsibility of a certain kind of object in the system, totally free from any implementation details. This is done with an interface definition.

Defining an interface describes how the rest of the system is allowed to interact with an object without providing any concrete details of how the object actually works on the inside. A good real-world analogy is how a piano, an electric keyboard, and to some degree, even a harpsichord and organ work. They each present the same interface to a musician. Each key is expected to play a certain pitch when pressed, and the keys are arranged in the same general pattern.

Piano

A musician can sit down at any instrument that provides the same interface and immediately play it without having to figure out how it works behind the scenes.

Defining and Using an Interface

Let’s carry on our example of a human and computer player of the same game, where a player can be asked for a move to make.

An interface version of what was once the Player base class might look like this:

interface IPlayer
{
    Direction MakeMove();
}

This looks quite a bit like a class definition. The first and most obvious difference is that we use the interface keyword instead of the class keyword. This signifies that we’re defining an interface and not an actual class with any implementation details.

You’ll also notice that this type starts with an I. Strictly speaking, you don’t need that I to define an interface. But virtually all C# programmers do so, so I recommend at least giving it a try for a while.

The MakeMove method is defined, but is not marked abstract or public. Members of an interface are already automatically public and abstract (unlike a class, which is private and non-abstract by default) so those keywords are not necessary.

The method does not have a defined body, but just simply ends with a semicolon.

We’ve defined just a boundary that the outside world can use to interact with something, without providing any details on how it should work on the inside.

note

You cannot put fields or constructors in an interface.

Any class that supports this interface–what many C# programmers call “implementing the interface”–need only indicate that they implement the interface and provide a definition for all members defined in the interface:

class HumanPlayer : IPlayer
{
    public Direction MakeMove()
    {
        // Same code as we had before, here.
    }
}

Unlike inheritance, we do not use the overrride keyword. We just simply express that we implement the interface (in the same way as specifying a base class) and provide a definition for each member.

Using the Interface Type

Just like with a base class, we can define variables that use the interface type:

IPlayer player1 = new HumanPlayer();
IPlayer player2 = new RandomAIPlayer();

Direction move = player1.MakeMove();

Mixing Interfaces and Base Classes

Every class can pick exactly one base class (object, if nothing else is specifically stated) but as many interfaces as you want. Thus, it is not uncommon to have a class definition that looks like this:

class Thing : BaseThing, ISomeInterface1, ISomeInterface2
{
    // ...
}

If you have multiple interfaces or base classes listed, you separate them with commas, and the base class must be the first item listed.

But otherwise, you can combine inheritance and interface implementations however you see fit. You can have one class that implements seven interfaces, 14 classes that all implement one interface, with four of those sharing a single base class that also implements the interface, or whatever makes sense for your specific situation.