Let me start by apologizing. I got writing and couldn't stop. This post is quite long, and in some places, I think I might have gotten off into the bushes and didn't explain myself so well. If you want the TL;DR version, go down to the last section about behaves-like-a vs. is-a-special-type-of relationships. That's probably the most relevant…
I definitely use them, though an argument could be made that interfaces are not required to make software.
Let me explain how I use them, and maybe that will inspire some of you guys as well.
Interfaces in the BCL
The first way I use interfaces is in the various interfaces that come with the BCL and the .NET Framework. For instance, in many places, IEnumerable<T> is a perfectly good collection for what I need, and while I'm usually actually using a specific concrete class like List<T>, if I can get away with it, I'll only require IEnumerable<T>, or return IEnumerable<T>, and work with the interface instead of the concrete class whenever possible. The BCL has a ton of interfaces defined, so even without making my own, I do find that I'm working with interface types pretty regularly.
Inversion of Control and Unit Testing
The second way that I use interfaces comes about because of a couple of development practices that I've been using more and more recently: dependency injection (DI)/inversion of control (IoC) and unit testing. (To be sure, I've been a fan of unit testing for a long time, but only really been sold on IoC in the last year or so.)
An example of how IoC works is probably in order. Some C# types (classes, structs,…) are self contained. They don't depend on other types to function. The Vector2 struct that everyone here is probably at least a little familiar with is a good example of this.
Other types depend on another type to get their job done. To invent an example where this is useful, let's say your game has a high score system where the data needs to be written out to a file, a database, or uploaded to a website for global rankings.
Good design would suggest that you should separate the tasks of accepting high scores from the game and keeping track of the overall list while the game is running from the actual saving/loading/retrieving to a permanent location. So you might define a HighScoreManager and a HighScoreSaver class. In this case, HighScoreManager will have a dependency on HighScoreSaver. As scores come in from the game, it will need to make requests of the HighScoreSaver class to make sure that they get permanently saved when they're supposed to.
So how does HighScoreManager get an instance of HighScoreSaver? I know a few years ago, my thought process would have been to just have it create a new instance of it and store it as an instance variable.
Inversion of Control is the principle that says it's better to have the HighScoreSaver class be given to HighScoreManager, instead of having it create its own. It can be given to it in the constructor as a parameter, or it can be set through a method or property, or HighScoreManager can do some sort of dynamic lookup to get a HighScoreSaver.
But the interesting thing is, because HighScoreManager doesn't have to create the actual instance anymore, it doesn't care what class actually implements the behavior, it only cares that it has something that can do the behavior it is expecting HighScoreSaver to do.
So in this case, to take the public methods in HighScoreSaver and pull it up into an interface (IHighScoreSaver) decouples HighScoreManager from the actual HighScoreSaver class.
This will be nice when we change from writing to a file to writing to a database, and have to swap out HighScoreSaver with HighScoreDatabaseSaver or HighScoreWebServiceSaver, because these can also just implement the IHighScoreSaver. Changes in where the high scores get saved no longer require changes to the HighScoreManager class because it doesn't care about the specific implementation of where it gets saved, and can instead just be satisfied knowing that it can send the save command to an IHighScoreSaver and that it will get saved.
This sort of structure is also very helpful in unit testing. Imagine if in order to write unit tests for HighScoreManager, you have to write to the file system or to a database or to a web service. Every time you run your unit tests, these changes will get pushed to the storage system. This is both time consuming (a really bad thing in unit tests, because you want the entire suite of tests to run in seconds, not minutes) and it has a way of making a mess of data when you run them, not to mention a lot can go wrong any time you try to access the file system or a database or a web service. Internet's down? Oh, I guess you can't run your unit tests.
With an IHighScoreSaver in place, our unit test can substitute in a mock instead of a real implementation, and we can test the behavior of HighScoreManager independent from the storage system, which is exactly what we want in a unit test.
Interfaces Create Application Seams
I'll give you another example where interfaces came in handy. Two jobs ago, we had a program that did a lot of matrix calculations. We were using a matrix library that we didn't particularly like (LAPACK) and we were going to switch. But the matrix library we were going to use (Math.NET Numerics) was new to all of us, and we had no clue if the performance was going to be any good, or if it had bugs that we couldn't work around. We wanted to be able to switch back to LAPACK if we needed to, or to be able to find a third matrix library.
In our initial attempt to swap LAPACK out for Math.NET, we discovered that LAPACK had spread throughout the entire application. It was going to be virtually impossible to extract LAPACK and replace it, but most importantly, we didn't want to have a huge headache if we ever made the change back or to something else.
The solution we used was to define an interface for the matrix. Throughout the program, we used the interface instead of Math.NET classes and then created an implementation that talked to Math.NET. We never switched away from Math.NET Numerics, but at one point in there, we swapped it out with a dummy implementation that I wrote really quickly just to prove out the idea that we had made it substitutable.
Creating an interface made pieces of our application swappable for something else (these application seams), and many times over, it has been a lifesaver for me.
Behaves-Like-A vs. Is-A-Special-Type-Of Relationships
I remember in one of my early software development classes, we talked about Is-A vs. Has-A relationships.
For instance, a car has a steering wheel, and has four tires. An elephant is an animal. In the class, the point of this discussion was to help you determine if you should use composition (something is a class-level instance variable) or inheritance (it uses a base class). Is-A relationships imply inheritance, whereas Has-A relationships imply composition. A {Car}} class might look like this:
public class Car
{
private SteeringWheel steeringWheel;
private Tire[] tires;
}
While the Elephant class would look like this:
public class Elephant : Animal
{
}
After many more years now of experience, I've come to learn that there are some subtleties in is-a relationships, and not everything that could be described as "X is a Y" should actually use inheritance. It's mostly the same thing, but after hearing it elsewhere, I've started calling it an Is-A-Special-Type-Of relationship, instead of just simply an Is-A. This nuanced behavior comes from learning a lot about the Liskov Substitution Principle. It's not worth going into a lot of detail on it right now, but suffice it to say that Is-A relationships and Is-A-Special-Type-Of relationships are slightly different, but close enough to the same idea for now, but I'll be calling it Is-A-Special-Type-Of relationship.
There's another type of behavior that I don't remember learning about in the same class, and that's what is often called a Behaves-Like-A relationship. This is for those cases when there is some sort of defined behavior that exists, and some classes will advertise themselves saying, "Hey, I can do that!" This is the perfect time for an interface.
I'm going to use the example of the ICloneable interface because it's a simple concept, but keep in mind that people generally say that ICloneable is not a good interface for various reasons. ICloneable basically says, "I can make copies of myself!" done by requiring a method called Clone() that returns an instance of object.
If a class can provide this behavior, they can implement ICloneable and then anybody using the class will know they can make clones of it.
You could do this with a base class. (Make ICloneable an abstract class that defines the Clone method as abstract.) This would require anything that can make clones to derive from this. But in C#, you can only derive from one base class. If all of your interfaces were abstract base classes, you wouldn't be able to be both cloneable and something else (like enumerable).
In cases like this, it makes sense to make the distinction, and use base classes whenever something is a special type of something else, and use interfaces when you just want to capture a specific behavior, or a related set of behaviors that anything could theoretically do.
This reason is probably the single best reason to use interfaces on occasion. If none of the other reasons I listed make sense to you or seem to apply in your specific situation, at least try to use this to separate things that behave-like vs. is-a-special-type-of something else.