You use an interface to implement polymorphism - the ability to use multiple different types with the same invocations.
To give an example, imagine several separate types which have a method to do the same thing. If we create an instance of each one, we invoke the method on them separately. This is valid code with no particular issues.
Code:
class Monster {
void Move(Direction d) { ... }
}
class Player {
void Move(Direction d) { ... }
}
Player p = new Player();
Monster m = new Monster();
p.Move(Direction.North);
m.Move(Direction.North);
A problem might come if I write another function which requires the use of the Move function, such as a path-finding function. I don't know whether or not to pass a Monster or a Player to the function. (Do I pass both, or do I pass an "object"? Or do I write a separate pathfinding function for each one, replacing Player with Monster?). None of these are particularly good ways of doing this. An example of how it might look passing 'object' around.
Code:
void MoveTo(Location destination, object playerOrMonster) {
if (playerOrMonster is Player) {
var player = (Player)playerOrMonster;
... //pathfinding stuff.
player.Move(...);
}
else if (playerOrMonster is Monster) { ... }
}
It's necessary to perform the type check and case, because you can't call .Move() on the 'object' type - and if you attempt to cast an object to the invalid type, you will get an exception.
And of course, if you have multiple other functions which invoke Move(), you need to add that kind of selection to each one individually.
The correct way to solve the problem is to take all the types which are required under a common interface, so that you can use the same invocation, regardless of the type you are using.
Code:
interface IMoveable {
void Move(Direction d);
}
class Monster : IMoveable {
public void Move(Direction d) { ... }
}
class Player : IMoveable {
public void Move(Direction d) { ... }
}
void MoveTo(Location destination, IMoveable playerOrMonster) {
... //Not necessary to determine which type playerOrMonster is.
playerOrMonster.Move(...);
}
Whether the code here calls Monster.Move or Player.Move is determined at runtime based on it's type - so you don't need to check it. The compiler converts interface calls to virtual calls, which are looked up in a "vtable" during runtime. This has a slight overhead compared with a regular, non-virtual method call, but the overhead is tiny, so it's not worth worrying about. Compared with the first MoveTo example though, where we would be manually checking the type - the virtual call is faster due to optimisations.
That's the basic idea of interfaces anyway. You can acheive similar results using classes with abstract or virtual methods too, which can also hold data and offer more flexibility. The choice between interface and abstract class is not always clear, and requires a bit of experience before you can make suitable decisions.