Team LiB   Previous Section   Next Section

14.1 Implementing an Interface

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

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

The optional attributes are not discussed in this book. Access modifiers (public, private, etc.) work just as they do with classes. (See Chapter 8 for more about access modifiers.) The interface keyword is followed by an identifier (the interface name). It is common (but not required) to begin the name of your interface with a capital I (IStorable, ICloneable, IGetNoKickFromChampagne, etc.). The optional base list is discussed in Section 14.4, later in this chapter.

The body of the interface is defined within braces, just as the body of a class would be.

Suppose you want to create an interface to define the contract for data being stored to a database or file. Your interface will define the methods and properties a class will need to implement in order to be stored to a database or 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);
}

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 a collection of IStorable objects, and to otherwise interact with your Document in this very general and well-understood way.

To implement the IStorable interface, use the same syntax as if 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 if there is an interface or base class named IStorable already defined.

Your definition of the Document class 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 14-1 illustrates defining and implementing the IStorable interface.

Example 14-1. Document class implementing IStorable
using System;

namespace InterfaceDemo
{
    // define the interface
    interface IStorable
    {
        void Read();
        void Write(object obj);
        int Status { get; set; }

    }

    // create a Document class that implements the IStorable interface
    public class Document : IStorable
    {
        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; }
        }

        // store the value for the property
        private int status = 0;
    }

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

      [STAThread]
      static void Main()
      {
         Tester t = new Tester();
         t.Run();
      }
   }
}
Output:
Creating document with: Test Document
Implementing the Read Method for IStorable
Document Status: -1

Example 14-1 defines a simple interface, IStorable, with two methods (Read() and Write()) and a property (Status) of type integer:

// define the interface
    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 (e.g., 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.

The methods are otherwise defined just like methods in a class: the return type (void), followed by the identifier (Write), followed by the parameter list (object obj), and ended with a semicolon.

An interface can define that the implementing class will provide a property (see Chapter 9 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 is a get() and a set():

int Status { get; set; }

Once again, it is up to the implementing class to provide the actual implementation of the get and set accessors, just as the implementing class must provide the implementation of the methods.

Once you've defined the IStorable interface, you can define classes that implement the 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.

// create a Document class that implements the IStorable interface
public class Document : IStorable
{

    // 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; }
    }

How your Document class fulfills the requirements of the interface, however, 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 14-1 implements the Status property by returning (or setting) the value of a private member variable, status.

    Team LiB   Previous Section   Next Section