Logo

Interfaces

Fundamentals

An Interface is like a blueprint. It defines some data and functionality that should exist within a type that implements the interface. An interface does not care about the implementation. It merely requires that it is implemented.

C# naming convention dictates that an interface start with a capital I, for interface.

Here's an example interface:

interface ISampleInterface
{
    void SampleMethod();
}

interface IOtherInterface
{
    string OtherMethod();
}

Unlike inheritance, a class can conform to multiple interfaces:

class SampleClass : ISampleInterface, IOtherInterface
{
    // ISampleInterface member implementation
    void SampleMethod()
    {
        // Method implementation
    }

    // IOtherInterface member implementation
    string OtherMethod()
    {
        // Other implementation
        return "Message";
    }
}

Later in code, you can create an instance of the SampleClass as an instance of the ISampleInterface:

ISampleInterface sample = new SampleClass();
sample.SampleMethod();

You can also have an interface inherit from another interface.

Let's create a Hawk class and a Penguin class. Both are animals. Both are birds, even. But, only one can fly.

Using Interfaces, we can accomplish this functionality is a clear, concise, and readable way:

class Animal {}

public interface IBird // For all birds
{
    void CleanFeathers();
}

public interface IFlyingBird : IBird // For all flying birds
{
    void Fly();
}

class Hawk : Animal, IFlyingBird
{
    public void CleanFeathers() {}
    public void Fly() {}
}

class Penguin : Animal, IBird
{
    public void CleanFeathers() {}
}

Let's look at an example that shows the benefit of interchangeable code using Interfaces. We'll use the IBird interface with a method:

public void CleanBird(IBird bird)
{
    bird.CleanFeathers();
}

We've created a method that accepts any bird in a type-safe fashion. Later, we can create an instance of the Hawk class and the Penguin class. Both of these instances are different types, but both implement the IBird interface:

var hawk = new Hawk();
CleanBird(hawk); // this works

var penguin = new Penguin();
CleanBird(penguin); // this also works

Let's look at another example. We'll use the IEnumerable interface:

// Here we have an array of strings
var emailArray = new [] { "a@me.com", "b@me.com" };

// Here we have a list of strings
var emailList = new List<string> { "a@me.com", "b@me.com" };

Both these variables are different types, but both implement the IEnumerable interface. So, let's create a method:

public void SendEmails(IEnumerable<string> emailAddresses)
{
    foreach(var emailAddress in emailAddresses)
    {
        //do something with the email
    }
}

Later in your code, you can call your SendEmails method using either your emailArray or emailList variables:

SendEmails(emailArray); // this works
SendEmails(emailList); // this also works

We've created a method that accepts either an Array or a List in a type-safe fashion.