Professor Mustard Programming
 

Programming 101 - Unit 05 - Advanced OOP

05e) Interfaces
 
The last new concept we'll look at in this unit is the interface. Interfaces are used in much the same way that abstract classes are used. By the end of this lesson, however, you'll understand the differences between them. Let's start by looking at a simple interface:

interface IAnimal
{
  void Speak();
}



An interface is comparable to an abstract class, in that you can't create instances of it. It only exists to define a "base" that other classes can inherit from. However, an interface takes this idea even further. Interfaces cannot have members, constructors, and static variables. They also can't have any methods with actual code in them. The only thing that interfaces are allowed to have are abstract, public methods. Because of this, you don't even use the words "abstract" or "public" on the interface's methods, because every method in there is automatically going to be abstract and public, anyway.

It is customary in most programming circles to prefix interface names with an "I". This is simply to differentiate them from classes, which tend to be named in similar ways. When the time comes for a class to actually inherit from the interface, it works almost the same as inheriting a class with abstract methods. Your inheriting class must include each method that is is defined in the interface. Observe:

class Animal: IAnimal
{
  public void Speak()
  {
  }
}



Notice something a little different? Even though Speak() is an inherited method, we didn't have to specify that it was an override. We can declare Animal's Speak() method as virtual if we want to, but it's not necessary. If we write the method without "virtual", like we did above, it produces the same effect as adding the "sealed" keyword on an ordinary override method. This is simply the way that interfaces work; the class that inherits from the interface gets to determine whether the methods will actually be virtual or not.

So, the next obvious question is, "why do we care?" With an abstract class, you can include as many concrete (non-abstract) methods as you want, and any class that inherits from it will benefit from that code. Interfaces are only good for defining the public methods that an inheriting class must have (in other words, its "interface"). So, why would we want to use an interface, when we could use an abstract class and include actual code for our inheriting classes to use?

As with most things in programming, interfaces involve a trade-off. Interfaces are limited to defining public, abstract methods, but they can be used in several ways that abstract classes can't.

For one thing, a struct can never inherit from a class, but a struct can inherit from an interface. Why won't a class work? Because, as we've discussed in previous units, classes and structs have a few fundamental differences between them. And it wouldn't be safe to allow structs to inherit from something that behaves differently from them. Interfaces, on the other hand, are so simple that both structs and classes can inherit from them without any worries.

Because both structs and classes can inherit from interfaces, it allows us to write methods that can accept both:

interface IRunnable
{
  void Run(int number);
}

// Animal class is IRunnable
class Animal: IRunnable
{
  public void Run(int number)
  {
  }
}

// InsectData struct is IRunnable
struct InsectData: IRunnable
{
  public void Run(int number)
  {
  }
}

// Therefore, you can pass Animal objects and InsectData
// structs into RunObject(), which accepts any IRunnable object
static void RunObject(IRunnable runnableObject)
{
  runnableObject.Run(5);
}



Perhaps the most fascinating aspect of interfaces is that they allow what is commonly referred to as "multiple inheritance", which is where a class actually inherits from more than one base. Here's an example:

interface IAnimal
{
  void Speak();
}

interface IPredator
{
  string Hunt();
}

class Animal: IAnimal, IPredator
{
  public void Speak()
  {
  }

  public string Hunt()
  {
    return "";
  }
}



Your classes can inherit from as many interfaces as you want, as long as you implement the methods that they define. You just separate each class you're inheriting from with a comma. So, why can't you inherit from multiple classes? Well, say that you were able to inherit from two classes, and they each contain a method called Run(). However, each Run() method contains different code. Which code is the inherited class actually supposed to use? There's no way to tell! Interfaces, on the other hand, never contain actual code; just method declarations. If you inherit from two interfaces, and they both contain a method called Run(), who cares? They're going to be exactly the same, right?

You can even combine class inheritance with interface inheritance, as long as you list the class first...

class Gull: Animal, IBird, IWingedCreature, IEggLayer
{
}



...in the above example, we see that class Gull inherits from the Animal class, as well as the IBird, IWingedCreature and IEggLayer interfaces. As long as you only inherit from one class, and the class is listed first, you can proceed to inherit from as many interfaces as you want.

Once a class inherits from an interface, the rest of the inheritance tree works as it normally would. All classes that inherit from the base class (which inherited from the interface) will have the base class's methods. If the base class made the methods virtual, the inherited classes will also have the option of overriding them. If the base class is abstract, it can still make its methods abstract, even the ones that it inherits from the interface. Of course, any class that inherits from the base class will be forced to write implementations for the abstract methods, as usual.

In addition to all this, any class in the inheritance tree will remember that it is associated with the interface at the base class's level. If Gull inherits from Animal, which inherits from IAnimal, Gull can still be used as an IAnimal. Also, any class in the inheritance tree is free to inherit from any new interfaces it wants.

So, how are interfaces commonly used? Because of their extraordinarily abstract nature, we rarely use them to describe nouns, since abstract classes offer more powerful options in this regard. Instead, interfaces are often used to describe abilities or properties that a class might have. The IRunnable interface described above is a perfect example of this. It doesn't say anything about what the inheriting classes will actually represent; it simply guarantees that they'll all have a public Run() method.

Likewise, the native .NET interface "IDisposable" doesn't tell you anything about the actual objects you'll be creating. It simply guarantees that anything that inherits from it will have a Dispose() method. Some other good examples for interfaces might be IPrintable in an inheritance tree of Document classes, IGuided in an inheritance tree of Missile classes, and IPlayableMedia, which would force every inheriting class to have Play(), Stop() and Pause() methods. Once again, interfaces are best used to define abilities and properties, and not the actual objects that will possess them. As an added bonus, their ability to be inserted anywhere in the inheritance tree allows you to build a normal architecture of abstract and inheriting classes, and then splice in interfaces at any point in the tree that you deem necessary.

The example below actually uses an IAnimal interface, and as you might recall, nouns aren't really the best way to use interfaces. However, I've already explained the best way to use interfaces in the paragraphs above. This code sample is mainly here to demonstrate what interfaces can do.



using System;

namespace E_Interfaces
{
  class Class1
  {
    static readonly string ln = Environment.NewLine;

    [STAThread]
    static void Main(string[] args)
    {
      Animal animal = new Animal();
      AnimalData data = new AnimalData();

      // Interfaces allow us to pass both classes and structs
      // into a common method that accepts an interface
      DoIAnimalAction(animal);
      DoIAnimalAction(data);
      data.Species = "Rat";
      DoIAnimalAction(data);

      PromptForExit();
    }

    static void DoIAnimalAction(IAnimal animal)
    {
      animal.Speak();
    }

    static void PromptForExit()
    {
      Console.Write(ln + "Program complete! Hit enter to exit...");
      Console.ReadLine();
    }
  }

  // The "public" keyword isn't needed in an interface's methods,
  // since every method in an interface is already considered public.
  // We are not allowed to define static methods in an interface.
  interface IAnimal
  {
    void Speak();
  }

  // It is customary to prefix interface names with "I"
  interface IPredator
  {
    string Hunt();
  }

  // We can inherit from as many interfaces as we need to!
  class Animal: IAnimal, IPredator
  {
    // Note that even though we're implementing the method
    // we defined in IAnimal, we don't have to use the
    // "override" keyword... we don't even need to use the
    // "virtual" keyword, if we don't want to.
    public virtual void Speak()
    {
      Console.WriteLine("I'm an animal!");
    }

    // We inherited this method from IPredator, but we didn't
    // make it virtual... so classes that inherit from Animal
    // won't have the option of overriding it.
    public string Hunt()
    {
      return "Hunted and ate a herbivore!";
    }
  }

  // Structs can also inherit from interfaces (even though
  // they can't inherit from classes)
  struct AnimalData: IAnimal
  {
    public string Species;

    public void Speak()
    {
      if( Species != null )
        Console.WriteLine("I'm a " + Species + "!");
      else
        Console.WriteLine("I'm an animal struct!");
    }
  }
}