Declaring a class to implement an interface is similar to deriving from a base class: The implemented interfaces appear in a comma-separated list along with the base class. The base class specifier (if there is one) must come first, but otherwise order is not significant. Classes can implement multiple interfaces but may derive directly from only one base class. An example appears in Listing 8.3.
Once a class declares that it implements an interface, all (abstract2) members of the interface must be implemented. An abstract class is permitted to supply an abstract implementation of an interface member. A non-abstract implementation may throw a NotImplementedException type exception in the method body, but an implementation of the member must always be supplied.
One important characteristic of interfaces is that they can never be instantiated; you cannot use new to create an interface, so interfaces do not have instance constructors or finalizers. Interface instances are available only by instantiating a type that implements the interface. Furthermore, interfaces cannot include static members.3 One key interface purpose is polymorphism, and polymorphism without an instance of the implementing type has little value.
Each (non-implemented4) interface member is abstract, forcing the derived class to implement it. Therefore, it is not possible to use the abstract modifier on interface members explicitly.5
When implementing an interface member in a type, there are two ways to do so: explicitly or implicitly. So far, we’ve seen only implicit implementations, where the type member that implements the interface member is a public member of the implementing type.
Explicitly implemented methods are available only by calling them through the interface itself; this is typically achieved by casting an object to the interface. For example, to call IListable.CellValues in Listing 8.4, you must first cast the contact to IListable because of CellValues’ explicit implementation.
The cast and the call to CellValues occur within the same statement in this case. Alternatively, you could assign contact to an IListable variable before calling CellValues.
To declare an explicit interface member implementation, prefix the member name with the interface name (see Listing 8.5).
Listing 8.5 implements CellValues explicitly by prefixing the property name with IListable. Furthermore, since explicit interface implementations are directly associated with the interface, there is no need to modify them with virtual, override, or public. In fact, these modifiers are not allowed. The method is not treated as a public member of the class, so marking it as public would be misleading.
Note that even though the override keyword is not allowed on an interface, we will still use the term “override” when referring to members that implement the interface-defined signature.
Notice that CompareTo() in Listing 8.5 does not include the IComparable prefix; it is implemented implicitly. With implicit member implementation, it is necessary only for the member to be public and for the member’s signature to match the interface member’s signature. Interface member implementation does not require use of the override keyword or any indication that this member is tied to the interface. Furthermore, since the member is declared just like any other class member, code that calls implicitly implemented members can do so directly, just as it would any other class member:
result = contact1.CompareTo(contact2);
In other words, implicit member implementation does not require a cast because the member is not hidden from direct invocation on the implementing class.
Many of the modifiers disallowed on an explicit member implementation are required or are optional on an implicit implementation. For example, implicit member implementations must be public. Furthermore, virtual is optional, depending on whether derived classes may override the implementation. Eliminating virtual will cause the member to behave as though it is sealed.
The key difference between implicit and explicit member interface implementation lies not in the syntax of the method declaration but rather in the ability to access the method by name through an instance of the type rather than via the interface.
When building a class hierarchy, it’s desirable to model real-world “is a” relationships—a giraffe is a mammal, for example. These are semantic relationships. Interfaces are often used to model mechanism relationships. A PdaItem “is not a comparable,” but it might well be IComparable. This interface has nothing to do with the semantic model; instead, it’s a detail of the implementation mechanism. Explicit interface implementation is a technique for enabling the separation of mechanism concerns from model concerns. Forcing the caller to cast the object to an interface such as IComparable before treating the object as comparable explicitly separates in the code the concepts of talking to the model and dealing with its implementation mechanisms.
In general, it is preferable to limit the public surface area of a class to be “all model” with as little extraneous mechanism as possible. (Unfortunately, some mechanisms are unavoidable in .NET. In the real world, for example, you cannot get a giraffe’s hash code or convert a giraffe to a string. However, you can get a Giraffe’s hash code [GetHashCode()] and convert it to a string [ToString()] in .NET. By using object as a common base class, .NET mixes model code with mechanism code, even if only to a limited extent.) Here are several guidelines that will help you choose between an explicit implementation and an implicit implementation.
Much of the decision-making regarding implicit versus explicit interface member implementation comes down to intuition. However, these questions provide suggestions about which issues to consider when making your choice. Since changing an implementation from implicit to explicit results in a version-breaking change, it is better to err on the side of defining interfaces explicitly, allowing them to be changed to implicit implementations later. Furthermore, since the decision between implicit and explicit does not have to be consistent across all interface members, defining some methods as explicit and others as implicit is fully supported.