Implementing an Interface

The syntax for defining an interface is very similar to the syntax for defining a class:

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

Tip

The optional attributes are beyond the scope of this book. In short, every .NET application contains code, data, and metadata. Attributes are objects that are embedded in your program (invisible at runtime) and contain metadata—that is, data about your classes and your program. You don’t need to worry about them for our purposes here.

Access modifiers (public, private, and so forth) work just as they do with classes. (See Chapter 7 for more about access modifiers.) The interface keyword is followed by an identifier (the interface name). It is recommended (but not required) to begin the name of your interface with a capital I (IStorable, ICloneable, IGetNoKickFromChampagne, and so on). We will discuss the optional base list later in this chapter.

Now, suppose you are the author of a Document class, which specifies that Document objects can be stored in a database. You decide to have Document implement the IStorable interface. It isn’t required that you do so, but by implementing the IStorable interface, you signal to potential clients that the Document class can be used just like any other IStorable object. This will, for example, allow your clients to add your Document objects to an array of IStorable references:

IStorable[] myStorableArray = new IStorable[3];

As we discussed earlier, the array doesn’t specifically need to know that it holds a Document object, just that it holds objects that implement IStorable.

To implement the IStorable interface, use the same syntax as though the new Document class were inheriting from IStorable—a colon (:) followed by the interface name:

public class Document : IStorable

You can read this as “define a public class named Document that implements the IStorable interface.” The compiler distinguishes whether the colon indicates inheritance or implementation of an interface by checking to see whether IStorable is defined, and whether it is an interface or base class.

If you derive from a base class and you also implement one or more interfaces, you use a single colon and separate the base class and the interfaces by commas. The base class must be listed first; the interfaces may be listed in any order.

public MyBigClass : TheBaseClass, IPrintable, IStorable, IClaudius, IAndThou

In this declaration, the new class MyBigClass derives from TheBaseClass and implements four interfaces.

Suppose that the definition of IStorable requires a void Read( ) method, and a void Write( ) method that takes an object. In that case, your definition of the Document class that implements the IStorable interface might look like this:

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. Example 13-1 illustrates defining and implementing the IStorable interface. Have a look at it first, and we’ll take it apart afterward.

Example 13-1. Implementing an interface simply requires implementing all of its properties and methods, in whatever way is best for your class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Example_13_1_ _ _ _Implementing_Interface
{
   interface IStorable
   {
       void Read( );
       void Write( object obj );
       int Status { get; set; }
   }

   public class Document : IStorable
   {

       public Document( string s )
      {
         Console.WriteLine( "Creating document with: {0}", s );
      }

#region IStorable

      public void Read( )
      {
         Console.WriteLine( "Executing Document's Read
                             Method for IStorable" );
      }

      public void Write( object o )
      {
         Console.WriteLine( "Executing Document's Write
                             Method for IStorable" );
      }

      // property required by IStorable
      public int Status { get; set;}

#endregion

   }

   class Tester
   {
      public void Run( )
      {
         Document doc = new Document( "Test Document" );
         doc.Status = -1;
         doc.Read( );
         Console.WriteLine( "Document Status: {0}", doc.Status );
      }

      static void Main( )
      {
         Tester t = new Tester( );
         t.Run( );
      }
   }
}

The output looks like this:

Creating document with: Test Document
Executing Document's Read Method for IStorable
Document Status: -1

Defining the Interface

In Example 13-1, the first few lines define an interface, IStorable, which has two methods (Read( ) and Write( )) and a property (Status) of type int:

interface IStorable
 {
     void Read( );
     void Write(object obj);
     int Status { get; set; }
 }

Notice that the IStorable method declarations for Read( ) and Write( ) do not include access modifiers (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. In addition, you must declare these methods to be public, and not static, when you implement the interface.

In the interface declaration, the methods are otherwise defined just like methods in a class: you indicate the return type (void), followed by the identifier (Write), followed by the parameter list (object obj), and, of course, you end all statements with a semicolon. The methods in the interface declaration have no body, however.

An interface can also require that the implementing class provide a property (see Chapter 8 for a discussion of properties). Notice that the declaration of the Status property does not provide an implementation for get( ) and set( ), but simply designates that there must be a get( ) and a set( ):

int Status { get; set; }

You can’t define member variables in an interface, but defining properties like this has the same practical effect.

Implementing the Interface on the Client

Once you’ve defined the IStorable interface, you can define classes that implement your interface. Keep in mind that you cannot 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. Thus, your Document class must provide both a Read( ) and a Write( ) method and the Status property:

public class Document : IStorable
{

This statement defines Document as a class that defines IStorable. We also like to separate the implementation of an interface in a region—this is a Visual Studio convenience that allows you to collapse and expand the code within the region to make the code easier to read:

#region IStorable
  //...
#endregion

Within the region, you place the code that implements the two required methods and the required property. In this case, we’re not really reading or writing anything. To keep things simple in the example, we’re just announcing to the console that we’ve invoked the appropriate method; you’ll have to use your imagination a bit:

public void Read( )
{
   Console.WriteLine( "Executing Document's Read
                       Method for IStorable" );
}

public void Write( object o )
{
   Console.WriteLine( "Executing Document's Write
                       Method for IStorable" );
}

Notice that the Write( ) method takes an instance of class object as a parameter, even though the method never uses it. Perhaps your specific implementation would do something with an object, but it doesn’t have to. Exactly how your Document class fulfills the requirements of the interface is entirely up to you.

Although IStorable dictates that Document must have a Status property, it does not know or care whether Document stores the actual status as a member variable or looks it up in a database. Example 13-1 implements the Status property with an automatic property (introduced in Chapter 8). Another class that implements IStorable could provide the Status property in an entirely different manner (such as by looking it up in a database).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset