It is possible to extend an existing interface to add new
methods or members. For example, you might extend ICompressible
with a new interface, ILoggedCompressible
, which extends the
original interface with methods to keep track of the bytes saved. One
such method might be called LogSavedBytes( )
. The following code creates a new interface named ILoggedCompressible
that is identical to
ICompressible
except that it adds the
method LogSavedBytes
:
interface ILoggedCompressible : ICompressible { void LogSavedBytes( ); }
Classes are now free to implement either ICompressible
or ILoggedCompressible
, depending on whether they
need the additional functionality. If a class does implement ILoggedCompressible
, it must implement all the
methods of both ILoggedCompressible
and also ICompressible
. Objects of
that type can be cast either to ILoggedCompressible
or to ICompressible
.
Example 13-4
extends ICompressible
to create
ILoggedCompressible
, and then casts
the Document
first to be of type
IStorable
, then to be of type
ILoggedCompressible
. Finally, the
example casts the Document
object to
ICompressible
. This last cast is safe
because any object that implements ILoggedCompressible
must also have implemented
ICompressible
(the former is a
superset of the latter). This is the same logic that says you can cast
any object of a derived type to an object of a base type (that is, if
Student
derives from Human
, then all Students
are Human
, even though not all Humans
are Students
).
Example 13-4. Extending interfaces
using System; namespace ExtendingInterfaces { interface ICompressible { void Compress( ); void Decompress( ); } // extend ICompressible to log the bytes saved interface ILoggedCompressible : ICompressible { void LogSavedBytes( ); } public class Document : ILoggedCompressible { public Document( string s ) { Console.WriteLine( "Creating document with: {0}", s ); } #region public void Compress( ) { Console.WriteLine( "Implementing Compress" ); } public void Decompress( ) { Console.WriteLine( "Implementing Decompress" ); } public void LogSavedBytes( ) { Console.WriteLine( "Implementing LogSavedBytes" ); } #endregion //ILoggedCompressible } class Tester { public void Run( ) { Document doc = new Document( "Test Document" ); ILoggedCompressible myLoggedCompressible = doc as ILoggedCompressible; if ( myLoggedCompressible != null ) { Console.Write( " Calling both ICompressible and " ); Console.WriteLine( "ILoggedCompressible methods..." ); myLoggedCompressible.Compress( ); myLoggedCompressible.LogSavedBytes( ); } else { Console.WriteLine( "Something went wrong! Not ILoggedCompressible" ); } } static void Main( ) { Tester t = new Tester( ); t.Run( ); } } }
The output looks like this:
Creating document with: Test Document Calling both ICompressible and ILoggedCompressible methods... Implementing Compress Implementing LogSavedBytes
Example 13-4
starts by creating the ILoggedCompressible
interface, which extends
the ICompressible
interface:
// extend ICompressible to log the bytes saved interface ILoggedCompressible : ICompressible { void LogSavedBytes( ); }
Notice that the syntax for extending an interface is the same as
that for deriving from a class. This extended interface defines only one
new method (LogSavedBytes( )
), but
any class implementing this interface must also implement the base
interface (ICompressible
) and all its
members. (In this sense, it is reasonable to say that an ILoggedCompressible
object
is-a ICompressible
object.)