In the implementation shown so far, the class that implements the interface (Document
) creates a member method with the same signature and return type as the method detailed in the interface. It is not necessary to explicitly state that Document
is implementing IStorable
, for example; the compiler understands this implicitly.
What happens, however, if the class implements two interfaces, each of which has a method with the same signature? This might happen if the class implements interfaces defined by two different organizations or even two different programmers. The next example creates two interfaces: IStorable
and ITalk
. ITalk
implements a Read( )
method that reads a book aloud. Unfortunately, this conflicts with the Read( )
method in IStorable
.
Because both IStorable
and ITalk
have a Read( )
method, the implementing Document
class must use explicit implementation for at least one of the methods. With explicit implementation, the implementing class (Document
) explicitly identifies the interface for the method:
void ITalk
.Read( )
Marking the Read( )
method as a member of the ITalk
interface resolves the conflict between the identical Read( )
methods. There are some additional aspects you should keep in mind.
First, the explicit implementation method cannot have an access modifier:
void ITalk.Read( )
This method is implicitly public. In fact, a method declared through explicit implementation cannot be declared with the abstract
, virtual
, override
, or new
keyword, either.
Most importantly, you cannot access the explicitly implemented method through the object itself. When you write:
theDoc.Read( );
the compiler assumes you mean the implicitly implemented interface for IStorable
. The only way to access an explicitly implemented interface is through a cast to the interface:
ITalk itDoc = theDoc as ITalk; if (itDoc != null) { itDoc.Read( ); }
Explicit implementation is demonstrated in Example 13-6. Note that there is no need to use explicit implementation with the other method of ITalk
:
public void Talk( )
Because there is no conflict, this can be declared as usual.
Example 13-6. Explicit implementation allows you to avoid conflicts when two interfaces have methods with the same name
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Example_13_6_ _ _ _Explicit_Interfaces { interface IStorable { void Read( ); void Write( ); } interface ITalk { void Talk( ); void Read( ); } // Modify Document to also implement ITalk public class Document : IStorable, ITalk { // the document constructor public Document(string s) { Console.WriteLine("Creating document with: {0}", s); } // Implicit implementation public virtual void Read( ) { Console.WriteLine("Document Read Method for IStorable"); } public void Write( ) { Console.WriteLine("Document Write Method for IStorable"); } // Explicit implementation void ITalk.Read( ) { Console.WriteLine("Implementing ITalk.Read"); } public void Talk( ) { Console.WriteLine("Implementing ITalk.Talk"); } } class Tester { public void Run( ) { // Create a Document object Document theDoc = new Document("Test Document"); IStorable isDoc = theDoc as IStorable; if (isDoc != null) { isDoc.Read( ); } // Cast to an ITalk interface ITalk itDoc = theDoc as ITalk; if (itDoc != null) { itDoc.Read( ); } theDoc.Read( ); theDoc.Talk( ); } static void Main( ) { Tester t = new Tester( ); t.Run( ); } } }
The output looks like this:
Creating document with: Test Document Document Read Method for IStorable Implementing ITalk.Read Document Read Method for IStorable Implementing ITalk.Talk
The first thing the program does is create an IStorable
reference to a Document
. Then it invokes the Read( )
method of IStorable
:
IStorable isDoc = theDoc as IStorable; if (isDoc != null) { isDoc.Read( ); }
Then you cast the document to an ITalk
interface, and invoke the Read( )
method of ITalk
:
ITalk itDoc = theDoc as ITalk; if (itDoc != null) { itDoc.Read( ); }
The output shows that both of these calls work as expected.
The next thing you do is to call the Read( )
and Talk( )
methods directly on the Document
:
theDoc.Read( ); theDoc.Talk( );
As you can see in the output, the Read( )
method defaults to the version from IStorable
, because that version is implicit. The Talk( )
method is the version from ITalk
, because that’s the only interface here with a Talk( )
method.