Section 8.1. Defining and Implementing an Interface


8.1. Defining and Implementing an Interface

The syntax for defining an interface is as follows:

 [attributes] [access-modifier] interface interface-name[:base-list]  {interface-body}

Don't worry about attributes for now; they're covered in Chapter 18.

Access modifiers, including public, private, protected, internal, and protected internal, were discussed in Chapter 4.

The interface keyword is followed by the name of the interface. It is common (but not required) to begin the name of your interface with a capital I (thus, IStorable, ICloneable, IClaudius, etc.).

The base-list lists the interfaces that this interface extends (as described in the next section, Section 8.1.1).

The interface-body describes the methods, properties, and so forth that must be implemented by the implementing class.

Suppose you wish to create an interface that describes the methods and properties a class needs, to be stored to and retrieved from a database or other storage such as a file. You decide to call this interface IStorable.

In this interface you might specify two methods: Read() and Write(), which appear in the interface-body.

interface IStorable {    void Read();    void Write(object); }

The purpose of an interface is to define the capabilities that you want to have available in a class.

For example, you might create a class, Document. It turns out that Document types can be stored in a database, so you decide to have Document implement the IStorable interface.

To do so, use the same syntax as if the new Document class were inheriting from IStorablea colon (:), followed by the interface name:

public class Document : IStorable {    public void Read() {...}    public void Write(object obj) {...}    // ... }

It is now your responsibility, as the author of the Document class, to provide a meaningful implementation of the IStorable methods. Having designated Document as implementing IStorable, you must implement all the IStorable methods, or you will generate an error when you compile. This is illustrated in Example 8-1, in which the Document class implements the IStorable interface.

Example 8-1. Using a simple interface
#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace SimpleInterface { // declare the interface    interface IStorable    {       // no access modifiers, methods are public       // no implementation       void Read( );       void Write( object obj );       int Status { get; set; }    } // create a class which implements the IStorable interface    public class Document : IStorable    {       // store the value for the property       private int status = 0;       public Document( string s )       {          Console.WriteLine( "Creating document with: {0}", s );       }       // implement the Read method       public void Read( )       {          Console.WriteLine(             "Implementing the Read Method for IStorable" );       }       // implement the Write method       public void Write( object o )       {          Console.WriteLine(             "Implementing the Write Method for IStorable" );       }       // implement the property       public int Status       {          get          {             return status;          }          set          {             status = value;          }       }    } // Take our interface out for a spin    public class Tester    {       static void Main( )       {          // access the methods in the Document object          Document doc = new Document( "Test Document" );          doc.Status = -1;          doc.Read( );          Console.WriteLine( "Document Status: {0}", doc.Status );       }    } } Output: Creating document with: Test Document Implementing the Read Method for IStorable Document Status: -1

Example 8-1 defines a simple interface, IStorable, with two methods (Read() and Write()) and a property (Status) of type integer. Notice that the property declaration doesn't provide an implementation for get( ) and set( ), but simply designates that there is a get( ) and a set( ):

int Status { get; set; }

Notice also that the IStorable method declarations don't include access modifiers (for example, public, protected, internal, private). In fact, providing an access modifier generates a compile error. Interface methods are implicitly public because an interface is a contract meant to be used by other classes. You can't create an instance of an interface; instead you instantiate a class that implements the interface.

The class implementing the interface must fulfill the contract exactly and completely. Document must provide both a Read() and a Write() method and the Status property. How it fulfills these requirements, however, is entirely up to the Document class. Although IStorable dictates that Document must have a Status property, it doesn't know or care whether Document stores the actual status as a member variable or looks it up in a database. The details are up to the implementing class.

8.1.1. Implementing More Than One Interface

Classes can implement more than one interface. For example, if your Document class can be stored and it also can be compressed, you might choose to implement both the IStorable and ICompressible interfaces. To do so, change the declaration (in the base list) to indicate that both interfaces are implemented, separating the two interfaces with commas:

public class Document : IStorable, ICompressible

Having done this, the Document class must also implement the methods specified by the ICompressible interface (which is declared in Example 8-2):

public void Compress() {    Console.WriteLine("Implementing the Compress Method"); } public void Decompress( ) {    Console.WriteLine("Implementing the Decompress Method"); }

8.1.2. Extending Interfaces

It is possible to extend an existing interface to add new methods or members, or to modify how existing members work. 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:

interface ILoggedCompressible : ICompressible {     void LogSavedBytes(); }

Effectively, by extending ICompressible in this way, you are saying that anything that implements ILoggedCompressible must also implement ICompressible.


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 ICompressible. Objects of that type can be cast either to ILoggedCompressible or to ICompressible.

8.1.3. Combining Interfaces

Similarly, you can create new interfaces by combining existing interfaces and, optionally, adding new methods or properties. For example, you might decide to create IStorableCompressible. This interface would combine the methods of each of the other two interfaces, but would also add a new method to store the original size of the precompressed item:

interface IStorableCompressible : IStorable, ILoggedCompressible {      void LogOriginalSize(); }

Example 8-2 illustrates extending and combining interfaces.

Example 8-2. Extending and combining interfaces
#region Using directives using System; using System.Collections.Generic; using System.Text; #endregion namespace ExtendAndCombineInterface {    interface IStorable    {       void Read( );       void Write( object obj );       int Status { get; set; }    } // here's the new interface    interface ICompressible    {       void Compress( );       void Decompress( );    } // Extend the interface    interface ILoggedCompressible : ICompressible    {       void LogSavedBytes( );    } // Combine Interfaces    interface IStorableCompressible : IStorable, ILoggedCompressible    {       void LogOriginalSize( );    } // yet another interface    interface IEncryptable    {       void Encrypt( );       void Decrypt( );    }    public class Document : IStorableCompressible, IEncryptable    {       // hold the data for IStorable's Status property       private int status = 0;       // the document constructor       public Document( string s )       {          Console.WriteLine( "Creating document with: {0}", s );       }       // implement IStorable       public void Read( )       {          Console.WriteLine(             "Implementing the Read Method for IStorable" );       }       public void Write( object o )       {          Console.WriteLine(             "Implementing the Write Method for IStorable" );       }       public int Status       {          get          {             return status;          }          set          {             status = value;          }       }       // implement ICompressible       public void Compress( )       {          Console.WriteLine( "Implementing Compress" );       }       public void Decompress( )       {          Console.WriteLine( "Implementing Decompress" );       }       // implement ILoggedCompressible       public void LogSavedBytes( )       {          Console.WriteLine( "Implementing LogSavedBytes" );       }       // implement IStorableCompressible        public void LogOriginalSize( )       {          Console.WriteLine( "Implementing LogOriginalSize" );       }       // implement IEncryptable       public void Encrypt( )       {          Console.WriteLine( "Implementing Encrypt" );       }       public void Decrypt( )       {          Console.WriteLine( "Implementing Decrypt" );       }    }    public class Tester    {       static void Main( )       {          // create a document object          Document doc = new Document( "Test Document" );          // cast the document to the various interfaces          IStorable isDoc = doc as IStorable;          if ( isDoc != null )          {             isDoc.Read( );          }          else             Console.WriteLine( "IStorable not supported" );          ICompressible icDoc = doc as ICompressible;          if ( icDoc != null )          {             icDoc.Compress( );          }          else             Console.WriteLine( "Compressible not supported" );          ILoggedCompressible ilcDoc = doc as ILoggedCompressible;          if ( ilcDoc != null )          {             ilcDoc.LogSavedBytes( );             ilcDoc.Compress( );             // ilcDoc.Read( );          }          else             Console.WriteLine( "LoggedCompressible not supported" );          IStorableCompressible isc = doc as IStorableCompressible;          if ( isc != null )          {             isc.LogOriginalSize( );  // IStorableCompressible             isc.LogSavedBytes( );    // ILoggedCompressible             isc.Compress( );         // ICompressible             isc.Read( );             // IStorable          }          else          {             Console.WriteLine( "StorableCompressible not supported" );          }          IEncryptable ie = doc as IEncryptable;          if ( ie != null )          {             ie.Encrypt( );          }          else             Console.WriteLine( "Encryptable not supported" );       }    } } Output: Creating document with: Test Document Implementing the Read Method for IStorable Implementing Compress Implementing LogSavedBytes Implementing Compress Implementing LogOriginalSize Implementing LogSavedBytes Implementing Compress Implementing the Read Method for IStorable Implementing Encrypt

Example 8-2 starts by implementing the IStorable interface and the ICompressible interface. The latter is extended to ILoggedCompressible and then the two are combined into IStorableCompressible. Finally, the example adds a new interface, IEncryptable.

The Tester program creates a new Document object and then uses it as an instance of the various interfaces. You are free to cast:

ICompressible icDoc = doc as ICompressible;

But this is unnecessary. The compiler knows that doc implements ICompressible and so can make the implicit cast for you:

ICompressible icDoc = doc;

On the other hand, if you are uncertain whether your class does implement a specific interface, you can cast using the as operator (described in detail later in this chapter), and then test whether the cast object is null (indicating that the cast was not legal) instead of assuming the cast and risk raising an exception.

ICompressible icDoc = doc as ICompressible; if ( icDoc != null ) {    icDoc.Compress( ); } else    Console.WriteLine( "Compressible not supported" );

8.1.3.1 Casting to extended interfaces

When the object is cast to ILoggedCompressible, you can use the interface to call methods on ICompressible because ILoggedCompressible extends (and thus subsumes) the methods from the base interface:

ILoggedCompressible ilcDoc = doc as ILoggedCompressible; if (ilcDoc != null) {     ilcDoc.LogSavedBytes( );     ilcDoc.Compress( );     // ilcDoc.Read( ); }

You can't call Read( ), however, because that is a method of IStorable, an unrelated interface. And if you uncomment out the call to Read( ), you will receive a compiler error.

If you cast to IStorableCompressible (which combines the extended interface with the Storable interface), you can then call methods of IStorableCompressible, ICompressible, and IStorable:

IStorableCompressible isc = doc as IStorableCompressible if (isc != null) {     isc.LogOriginalSize( );  // IStorableCompressible     isc.LogSavedBytes( );    // ILoggedCompressible     isc.Compress( );         // ICompressible     isc.Read( );             // IStorable }



Programming C#(c) Building. NET Applications with C#
Programming C#: Building .NET Applications with C#
ISBN: 0596006993
EAN: 2147483647
Year: 2003
Pages: 180
Authors: Jesse Liberty

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net