In my session on .NET Interfaces (IEnumerable, ISaveable, IDontGetIt: Interfaces in .NET) we take a look at how to create and use interfaces. But one question comes up repeatedly: When you let Visual Studio help you implement an interface (by right-clicking on the interface name), you get the option to "Implement Interface" or "Implement Interface Explicitly" -- so what does it mean to explicitly implement an interface?
Let's take a look. As we'll see, explicit implementation is generally optional; it all depends on what we are trying to do with the code.
Multiple Interfaces Using the Same Implementation
One reason people think we need to explicitly implement interfaces is if we have two interfaces with duplicate members (that is, one or more members that have the same method signature in each interface). Let's consider the following interfaces:
For this, we have 2 different interfaces that both contain the same method signature. Your first reaction may be that if we have a single class that implements both interfaces, we need to implement them explicitly to avoid the name conflict. But that is not necessarily the case.
Here is a sample implementation:
What we see here is a single implementation of the "Save" method. And that may be perfectly acceptable. Let's think about this for a moment. All we really care about is that the contract is fulfilled with the interfaces -- that is, that the class implements the members that are specified by the interfaces. If it turns out that the Save method of ISaveable and the Save method of IPersistable can have the same implementation in our class, then that's perfectly fine. The interfaces just care that the Save method is there.
To see that everything is fine, let's put together a quick console app that shows the different ways that we can call the Save method:
The first section creates 3 variables -- one each for the Implementation class, the ISaveable interface, and the IPersistable interface. The first three WriteLine calls show the output of calling Save with each of these variables.
The last three WriteLine calls show what happens when we cast the Implementation class to the interface and then call Save. Our output from this app is no real surprise:
Since we only have a single Save implementation, that is the output for each of the calls. Again, if this meets our functional needs, then we do not need to explicitly implement the interfaces.
Explicit Implementation
If we decide that the functionality for the interface implementations need to be different, then we can explicitly implement the interface. If we were to do this for both interfaces of our Implementation class, it would look something like this:
If you right-click on "ISaveable" and select "Explicitly Implement Interface", you will get the "string ISaveable.Save()" stub. If we do the same for the IPersistable interface, we end up with three different implementations of the Save method.
Let's take a look at the output from our console app now (the code for the console app remains the same, only the Implementation class has been updated):
Notice the difference. If we call Save on the "Implementation" variable, we get different results than if we call Save on the "ISaveable" variable or the "IPersistable" variable. The same is true if we take our "Implementation" variable and cast it to the different interface types.
We can also mix-and-match the non-explicit and explicit implementation. If we wanted, we could explicitly implement some members, but not others. The non-explicit implementations would behave like the previous sample (where we had no explicit implementations) and the explicit would behave like this sample.
Explicit Implementation May Be Required
In the samples above, explicit implementation is optional -- it is dependent upon our business needs. But there are situations where explicit implementation may be required. What if the signatures of the interface members don't quite match?
Notice that one of the Save methods returns void and the other returns string. This means that we would run into problems if we tried to do a non-explicit implementation. That's because we are not allowed to overload methods in a class based on the return types alone; they need different parameters in order to be overloaded. This means that in our implementation we must have at least one explicit implementation:
Here we have explicitly implemented the IPersistable Save method. In this case, we are simply calling the main Save method (which has the ISaveable signature) because our functionality is the same for our simple example.
Wrap Up
So, what we've seen is that implementing an interface explicitly is optional in a number of situations. If the implementation is the same, then we can just use a non-explicit implementation. Explicit implementation allows us to have different functionality for each interface implementation, and it also allows us to disambiguate interface members that may have signatures that differ in return value only.
Interfaces are a powerful tool in the .NET developer's toolbox. They allow us to add abstraction to our code, let our code be more extensible, and add seams to our code that can facilitate fakes and mocks for unit testing. Explicit implementation is another aspect of interfaces that we can use when we have multiple interfaces with the same or similar members.
Happy Coding!
No comments:
Post a Comment