One of the key components of Object-oriented languages like Java and C# is the ability to write classes using interfaces, which standardize method definitions and enable enhanced polymorphism. We’ll discuss what they are, and how to use them.
Formalized Polymorphism
Interfaces are basically classes without code. The can define properties and methods (though not fields directly) without actually storing data or writing an implementation for functions.
For example, .NET’s IEnumerable interface is very common. All the it requires is a function that returns an IEnumerator, which can be used to iterate over a collection.
Every collection in .NET implements this interface. In fact, most collections will implement a lot of interfaces, all standardizing the methods used to work with them.
The benefit of doing it this way is that all the methods are standardized in the interface, so that any time you want something to accept many kinds of classes, you can do so by accessing the interface methods.
For example, foreach loops under the hood really just use .GetEnumerator(), so they will support any type of collection that implements IEnumerable, like Lists, Dictionaries, and HashSets.
You can think of Interfaces as being a replacement for base classes. Base classes are still very useful, but requiring every class to inherit and override methods can be clunky. Plus, you can only have one base class, but you can implement any number of interfaces.
Interfaces can also be used in place of type parameters. For example, you may have a custom collection, CustomList < T > . This works for any type, but if you wanted to call a specific method on each element, you can’t, since the compiler has no idea if the type being used supports that method. The only options you’ll get will be methods for basic objects.
However, if you were to instead ditch the generic type parameter and use an interface, you can call a method on the items. The collection will still support any kind of type, though now each item you intend to put in it will need to implement the interface.
In general, you should use interfaces when you’re reusing the same methods for many classes that don’t inherit from each other, and you’d like to be able to write polymorphic code that doesn’t care about the specific underlying class.
Using Interfaces
Interfaces are pretty simple to use. They use the same inheritance syntax as base classes, a colon after the class definition. Note that if you want to use an interface with a base class, the base class must come first, followed by a comma and then any interfaces it implements.
Once you add the interface definition, you’ll probably get an error. Visual Studio will tell you that you’re not actually implementing the interface properties and methods. This can actually be very useful, as if you make any changes to the interface, you’ll need to go and update all classes that implement it.
Visual Studio is pretty fancy, and if you click “show potential fixes,” you’ll get an option for VS to automatically add the interface boilerplate to your class. You’ll obviously need to replace the NotImplementedExceptions.
You can also implement interfaces explicitly. This has the same effect, but with different syntax that makes it clear which interface the method or property is coming from. This looks pretty weird at first glance, but can be useful if you’re implementing a lot of interfaces.
Writing Your Own Interfaces
Interface definitions are pretty much the same as class definitions, though each method will not have a body, and you’ll need to use “interface,” instead of “class,” obviously. Much like classes, interfaces can also use generic type parameters, which can be useful for custom collections.
You can use the { get; set; } syntax to specify properties implicitly, which also works when implementing them in the actual class:
You can also have interfaces inherit each other. For example, this ICustomList interface could be a lot more useful if it simply including all other definitions that List uses, but adds a few custom ones. Now, when you go to use the interface, you’ll need to implement all the interface members from every interface it inherits from.