Interfaces are an integral part of .NET, yet a lot of new .NET developers (especially those coming from weakly-typed languages, or languages where interfaces aren't supported) have difficulty in understanding why they're needed and how (and when) they're used. If you find yourself in that group you're in good company. It's not a difficult concept but it can be a bit foreign to developers new to them.
Let's get the textbook definition out of the way, then take a look at what it really means (and why you should even care). From Wikipedia:
"Interface generally refers to an abstraction that an entity provides of itself to the outside. This separates the methods of external communication from internal operation, and allows it to be internally modified without affecting the way outside entities interact with it, as well as provide multiple abstractions of itself. It may also provide a means of translation between entities which do not speak the same language, such as between a human and a computer."
Hmm - OK. While technically correct that doesn't really help, does it? Did your eyes glaze over like mine did while reading that? Maybe if we back up a bit and look at what a class is we might be able to make some sense of this.
The Basics
A class is a collection of methods, properties, and events. It is assumed that each of these methods, properties, and events "do something". At least in most cases (we're going to ignore the idea of an abstract class for right now). So far, so good. You can create a new instance of this class and do things with it, pass it around to other classes as a parameter, etc. In a weakly typed language (also known as a "dynamic language") you can pretty much pass any type of class around without having to worry about the type - as long as your code doesn't try to access a property or method that object doesn't have, you're good to go. The upside to this is that it's pretty flexible. The downside to this is that mistakes aren't caught at compile time - your app. just blows up at runtime (so you'd better test!). Strongly-typed languages take a different approach: you must define the types that can be passed as parameters to methods. That's OK as long as there is only one specific type your method acts on, or the type you're passing in inherits from the base type of the parameter - it's pretty straightforward. The upside is that mistakes (like trying to access a property or method that doesn't exist) is caught at compile time. The downside is that it's not as flexible as a dynamic language.
So what do I mean by "not as flexible"? On the surface it seems pretty reasonable that a strongly-typed method only accepts specific types - how else would the compiler (and you) know that it's safe to access a specific method or property? It doesn't. It only can accept that specific type of class, or any subclass of that same type.
Why is it OK to accept a subclass of the type? Well, the compiler can be sure that the subclass has the same exact properties & methods as its parent (it doesn't care if you add more of them or override the behavior).
But what if it doesn't? What if you had a class that wasn't of the correct type necssary to pass into a method and it didn't inherit from that type either? Let's take a look at an example of this. I wanted to keep it simple enough to understand, but have it be a REAL scenario (not some contrived example). Those goals are a bit difficult to balance so I error'd on the real scenario side of things. Hopefully it'll help you understand WHY some things are the way they are in the framework in addition to understanding interfaces.
A Real Example
For example, let's suppose we create a collection class that can have a collection of objects (that are all of the same type) and we want to have a method which can sort them. You want to write a generic Sort routine on your collection class that can sort any kind of collection of objects, as long as they're the same type. The first issue you'd run into is, "How do I generically create some code which can compare ANY type?" Remember - you have to be able to compare strings, numbers, date/times, maybe custom types someone may have created, etc. So it's not really possible to be able to compare ANY type. You could cover the basic types and then require the user to subclass your collection for any other custom types. It's a bit clunky, but it would work.
What if, instead, we decided that we'd have another class responsible for doing the sorting. We'd provide a default implementation and if you had your own custom types that needed to be sorted you could pass in your own implementation. That's a bit better. The developer would still need to subclass from our default implementation (since we've still got the strongly-typed issues here - again, dynamic languages don't have this "issue"). We could have the developer pass in their version of the class which does the comparison into the Sort() method. The passed in class would have a Compare() method and let's say it takes two parameters of the types of the same type of object and returns an integer value indicating whether one of the objects is less than, greater than, or equal to the other.
Let's take a look at what the code might look like for all of this:
public class Comparer
{
public int Compare(object x, object y)
{
// Code to do comparisons here
}
}
public class SampleArrayList
{
public virtual void Sort()
{
this.Sort(new Comparer());
}
public virtual void Sort(Comparer comparer)
{
// Do sorting here. Call out to comparer object to do the actual comparison
}
}
SampleArrayList list = new SampleArrayList();
// Add items to the list
list.Sort(); // default implementation
list.Sort(new SomeComparerSubclass()); // A custom sort
That looks OK. If I wanted to create my own comparison class all I'd do is subclass from Comparer and override Compare() with my own implementation. But let's take a step back and think about this for a bit. If you create a new class and want a method that can compare one instance of that class to another instance of it, where's the best place for the code? Doing comparison's is a pretty fundamental capability of a class - we do it all the time with string's, numbers, datetimes, etc. so clearly it belongs with the class, right? Your only other alternative would be to put it into another class that acts as a kind of "helper". That's effectively what we've forced on any developers that want to use the sorting capability of our collection for their own custom classes. They MUST create a helper class to do a comparison or they'd all have to inherit from our Comparer class (and use it as their base object). That might not be the end of the world but we're only talking about a single example - there are a lot of other places where this scenario comes up.
OK, having to create another class which only does a comparison for a specific type isn't all that great. Since it makes more sense to have a comparison handled by the object being compared, let's say we add a method called CompareTo() to any class when we want to be able to compare it against another instance. In our Comparer class we'll call that method and, hey, our comparer class is now a bit more generic right? We know that in .NET all objects inherit from "System.Object" so having our Comparer class's Compare() method accept "objects" means we can use it with ANY type. I don't actually have to create a new class for each and every item I'm going to compare! But wait - we're STILL going to have the problem with calling the CompareTo() method on our class in the collection; we're back to having to inherit from a common base class.
public class Comparer
{
public int Compare(object x, object y)
{
// Code to do comparisons here
return x.CompareTo(y.CompareTo());
}
}
But now how can we call the CompareTo() method on those objects? How do we even know if those objects have a CompareTo() method? Since .NET is strongly typed you can't do this - it won't even compile. The class type "object" doesn't have a "CompareTo" method. The .NET compiler knows that and it won't let you do that.
This is begining to feel like a circular issue, isn't it?
Breaking the Cycle
OK, now what? Well, you could jump through a lot of hoops and use reflection to make the method call for you. But it's a lot of work. In a dynamic language (such as Visual FoxPro) code like that is perfectly valid - I can pass any object type into a method like that and access any properties or methods and VFP doesn't care. If the object doesn't have those properties or methods it will just blow up at runtime. I'm a good developer so I won't pass in something that would blow up. .NET sucks - it makes things so difficult.
You're basically stuck at this point - you can't do what you'd like to do in .NET. If .NET supported the idea of multiple inheritance (where class C could inherit from Class A and from Class B at the same time), we'd be able to still make this work. But it doesn't.
Interfaces to the Rescue
That's where interfaces come in (finally!). Let's take another stab at a more concrete definition of an interface: An Interface is a definition of the types of properties, events, and methods a class needs to implement. It's nothing more than a list of properties, events, and methods (and the various types associated with them) that a class has to have. While we can't inherit from multiple classes in .NET, we can inherit from multiple interfaces.
Getting back to our example, we could define an interface that consists of a public method called CompareTo that accepts one parameter of the "System.Object" type ("object" for short). This method needs to return an integer: Less than zero means this instance is less than the object passed in, zero means it's equal, and greater than one means it's greater than the passed in object.
Here's what it might look like:
public interface IComparable
{
int CompareTo(object obj);
}
While we're at it, it seems like we might want to do something similar to our Comparer class:
public interface IComparer
{
int Compare(object x, object y);
}
Now all we'd need to do is make sure that any class that we want to compare inherits from the IComparer interface and then "implements" the specified properties, methods, and events (PEM). That is, you need to make sure your class has all of the same PEM's as the interface. Then you use the interface as the parameter type instead. Since we can inherit multiple interfaces on a given type we have a way of giving our classes the cameleon-like ability of appearing as exactly the right type to methods, regardless of what class it really inherits from. We'd do the same thing for any class which can compare two different classes.
So we can rewrite the above code:
public class Comparer : IComparer
{
public int Compare(object x, object y)
{
// Code here which compares x to y and returns integer
}
}
public class MyCustomClass : IComparable
{
public int CompareTo(object obj)
{
}
public int CompareTo(MyCustomClass obj)
{
}
}
And we rewrite the Sort() method on our collection class SampleArrayList to accept objects of type "IComparer" instead of "Comparer":
public void Sort(IComparer comparer)
{
// Code here that iterates over the collection and class Compare()
// with two of the items in our internal list.
}
Suddenly all of this starts working again - you get compiler-time checking to make sure things don't blow up, Intellisense works, etc. In fact the compiler verifies that you have, in fact, done all of this correctly - if your classes don't implement IComparer or IComparable it will let you know. You have the ability to create a custom class that compares objects in different ways (which makes it easy to come up with different ways of sorting, ex. ascending, descending) and have generic code which will work in most cases.
Conclusion
The scenario I described above plays out throughout the .NET framework. Fundamental things like garbage collection are handled via the IDisposable interface, iterating over a collection (think foreach) is handled by IEnumerable, comparing objects (like we described above) is handled by IComparer and IComparable. Interfaces are used extensively. Hopefully you've gotten a feel for why they're needed and how they're used inside of .NET.
Originally published in Universal Thread Magazine, April 2009