Skip to content

F: Interfaces

Concepts in Action

Imagine you and your friends are in a band. Each of you plays a different instrument: you might play the guitar, one friend plays the drums, another plays the keyboard, and so on. But even though you each play a different instrument, you all share something in common: you all know how to make music.

In C#, an interface is a bit like the role each of you plays in the band. It doesn’t matter what instrument you play (or in coding terms, what class you are), as long as you know how to make music (or fulfill the “contract” set by the interface).

The benefit of this approach becomes clear when you need all your musicians to play. You don’t need to know whether a musician plays the guitar, the drums or the keyboard; as long as they implement the interface, you can instruct them to play. This shows the power of polymorphism.

Understanding and applying interfaces can make your code more organized and manageable. It allows you to generalize behavior across different classes, making your code more modular and adaptable. It’s like having a universal remote that can control a TV, a stereo, and a DVD player because they all follow the same interface for receiving signals! In game development, this power of abstraction and generalization helps in managing complexity as your game grows.

Introduction

Consider the above case of a musician signing a contract. It promises that it will be able to Play an instrument, regardless of what that instrument is.

Similarly, an interface in C# is a contract that a class signs, agreeing to implement certain functions or properties. The interface doesn’t care how these functions or properties are implemented, just that they are. This allows different classes to be used interchangeably in certain contexts, much like how different musicians could fulfill the same contract in their own unique ways.

Prior C# knowledge required

To fully grasp the concept of interfaces in C#, it would be beneficial if you have a basic understanding of the following concepts:

C# Basics: Familiarity with the basics of C# syntax, including variables, data types, and control structures. Classes and Objects: Understand what classes are and how objects are instances of these classes. Inheritance: Having a grasp of inheritance in C#, since interfaces can be thought of as a form of multiple inheritance. functions: Understand what functions are and how they are defined and called in C#. Polymorphism: Interfaces are a fundamental part of achieving polymorphism in C#. You can refresh these concepts by reading the previous articles:

Definition

An interface is like a contract or a promise. When a class says it implements an interface, it is saying “I promise to have these functions or properties, just like the interface says I should”. It doesn’t matter what the class is or what it does, as long as it keeps this promise.

Furthermore, a class can have only one base class, so a dog can only ever have one base class, let’s say Animal. However, C# does not restrict the number of interfaces a class can implement. This means that a class can have behaviors from multiple interfaces. For example, a class Dog could implement interfaces IDogBehaviors, IPetBehaviors, and IAnimalBehaviors all at the same time. Each of these interfaces could define different methods that a Dog should have.

In other words, multiple interfacing allows a single class to take on many roles, depending on the context. This provides a high degree of flexibility and opens up many possibilities for designing versatile and reusable code.

Differences to JavaScript

In JavaScript, there’s no direct equivalent to C#’s interfaces. JavaScript is a dynamically typed language, and its flexibility allows objects to be shaped and reshaped at runtime, which somewhat reduces the need for formal interfaces.

Similarities to JavaScript

JavaScript doesn’t explicitly have interfaces like C#. However, you can achieve a similar design pattern using objects and functions. In JavaScript, you typically create an object that has certain properties and methods, and then use that object wherever you want. As long as an object has the required properties and methods, you can use it interchangeably with other objects of the same “kind”. This does mean that there is no way that Javascript can enforce such behavior in the way that C# does. This makes C#’s implementation of interfaces much less error prone.

Examples

// Defining the IMusician interface
public interface IMusician
{
    void Play();
}

// Defining the Guitarist class
public class Guitarist : IMusician
{
    public void Play()
    {
        Debug.Log("Playing the guitar.");
    }
}

// Defining the Drummer class
public class Drummer : IMusician
{
    public void Play()
    {
        Debug.Log("Playing the drums.");
    }
}

// Defining the Pianist class
public class Pianist : IMusician
{
    public void Play()
    {
        Debug.Log("Playing the piano.");
    }
}

// Now, let's create an array of musicians and make each of them play their instrument
IMusician[] band = new IMusician[3];
band[0] = new Guitarist();
band[1] = new Drummer();
band[2] = new Pianist();

for (int i = 0; i < band.Length; i++)
{
    band[i].Play();
}

Note that in this piece of code, Guitarist, Drummer, and Pianist can each be treated as an IMusician, because they all adhere to this interface. This is incredibly powerful because it allows for great flexibility in how we design and use our classes. We can have a collection of different IMusician objects, each of which could be an instance of a different class, and we can treat them in the same way when it comes to their IMusician behavior.

This makes our code more modular, reusable and maintainable. If we ever need to add another type of musician, say a Violinist, all we need to do is to create a Violinist class that implements the IMusician interface. There would be no need to modify the existing classes or the parts of our code that use the IMusician interface. This concept, which is a key part of polymorphism, helps us write more flexible and future-proof code.

Furthermore, the use of interfaces makes it easier to collaborate and work on large projects. For example, if you’re developing a music simulation game with a team, and you’re responsible for the “concert” part of the game, you don’t need to know the specifics of how a Guitarist, Drummer, or Pianist play their instrument. You only need to know that they are IMusicians and you can call the Play() method on them. This decouples the implementation details from the way the objects are used, making the system easier to understand and modify.

This can become especially powerful when a class decides to implement multiple interfaces. Let’s consider our Musician example. Suppose we now have another interface, ISongwriter, which contains a WriteSong() method. We could have a class SingerSongwriter that implements both IMusician and ISongwriter. It’s like this class is making two promises - it promises to play an instrument like a IMusician and to write a song like an ISongwriter:

public interface ISongwriter
{
    void WriteSong();
}

public class SingerSongwriter : IMusician, ISongwriter
{
    public void Play()
    {
        // Code for playing an instrument...
    }

    public void WriteSong()
    {
        // Code for writing a song...
    }
}

Key Takeaways

  • Contract: Think of an interface as a promise. If a class says it will use an interface, it’s like it’s saying “I promise to have these abilities”. For example, if a Musician class uses an IInstrumentPlayer interface, it promises to be able to play an instrument.

  • Many Promises: A class can make many promises (or use many interfaces). So a Musician could promise to play an instrument and also promise to sing, by using both IInstrumentPlayer and ISinger interfaces.

  • Grouping by Ability: Interfaces let us treat different classes that have the same abilities in the same way. So if Guitarist, Drummer, and Pianist all use the IInstrumentPlayer interface, we can ask each of them to PlayInstrument() in the same way, even though they play different instruments.

  • Making Changes Easier: Interfaces make it easier to change your code. If you decide you want a Musician to have a new ability, you can just create a new interface (or promise) for that ability, and then have Musician use that interface. You don’t have to go and change every Musician.

  • Keeping Things Separate: Interfaces help keep different parts of your code from getting too tangled up with each other. By using an interface, a class can use abilities from elsewhere in the code without having to know all the details about how those abilities work.

  • Working Together: Interfaces help different parts of your code work together. If two classes both use the same interface, they can use each other’s abilities, even if they’re part of different systems or libraries. This is because they both made the same promise about what abilities they have.

Further Reading

Microdoft: Interfaces

Tutorials Teacher: Interfaces


Last update: May 16, 2023