Previous Section  < Day Day Up >  Next Section

3.9. Interfaces

Syntax:


[attributes] [modifiers] interface  identifier [:baselist]

{interface body} [;]


The syntax of the interface declaration is identical to that of a class except that the keyword interface replaces class. This should not be surprising, because an interface is basically a class that declares, but does not implement, its members. An instance of it cannot be created, and classes that inherit from it must implement all of its methods.

This sounds similar to an abstract class, which also cannot be instantiated and requires derived classes to implement its abstract methods. The difference is that an abstract class has many more capabilities: It may be inherited by subclasses, and it may contain state data and concrete methods.

One rule of thumb when deciding whether to use a class or interface type is the relationship between the type and its inheriting classes. An interface defines a behavior for a class梥omething it "can do." The built-in .NET ICloneable interface, which permits an object to create a copy of itself, is an example of this. Classes, on the other hand, should be used when the inheriting class is a "type of " the base class. For example, you could create a shape as a base class, a circle as a subclass of shape, and the capability to change the size of the shape as an interface method.

Aside from their differing roles, there are numerous implementation and usage differences between a class and an interface:

  • An interface cannot inherit from a class.

  • An interface can inherit from multiple interfaces.

  • A class can inherit from multiple interfaces, but only one class.

  • Interface members must be methods, properties, events, or indexers.

  • All interface members must have public access (the default).

  • By convention, an interface name should begin with an uppercase I.

Creating and Using a Custom Interface

Listing 3-15 demonstrates how to define, implement, and program against a simple custom interface:

Listing 3-15. Interface Basics

public interface IShapeFunction

{

   double GetArea();    //  public abstract method

}



class Circle : IShapeFunction   // Inherit Interface

{

   private double radius;

   public circle( double rad)

   {

      radius = rad;

   }

   public double GetArea()

   {

      return ( Math.PI*radius * radius);

   }

   public string ShowMe()

   {

      return ("Circle");

   }

}



class Rectangle: IShapeFunction      // Inherit interface

{

   private double width, height;

   public rectangle( double myWidth, double myHeight)

   {

      width= myWidth;

      height= myHeight;

   }

   public double GetArea()

   {

      return (width * height);

   }

}

class MyApp

{

   public static void Main()

   {

      Circle myCircle = new Circle(4);

      // Interface variable that references a circle object.

      IShapeFunction myICircle = myCircle;

      Rectangle myRectangle = new Rectangle(4,8);

      // Place shape instances in an array

      IShapeFunction[] myShapes = {myCircle, myRectangle};

      // The next two statements print the same results

      MessageBox.Show(myCircle.GetArea().ToString());

      MessageBox.Show(myShapes[0].GetArea().ToString());

      MessageBox.Show(myCircle.ShowMe());   // class method

      MessageBox.Show(myICircle.GetArea().ToString());

      // The following will not compile because myICircle can

      // access only members of the IShapeFunction interface.

      MessageBox.Show(myICircle.ShowMe());

   }

}


The declaration of the interface and implementation of the classes is straightforward: A Circle and Rectangle class inherit the IShapeFunction interface and implement its GetArea method.

Conceptually, it is useful to think of a class that implements one or more interfaces as having multiple types. It has its own innate type, but it also can be viewed as having the type of each interface it implements. Consider this code in the MyApp class:


Circle myCircle = new Circle(4);

IShapeFunction myICircle = myCircle;


The first statement creates an instance of the Circle class. The second statement creates a variable that refers to this circle object. However, because it is specified as an IShapeFunction type, it can only access members of that interface. This is why an attempt to reference the ShowMe method fails. By using the interface type, you effectively create a filter that restricts access to members of the interface only.

One of the most valuable aspects of working with interfaces is that a programmer can treat disparate classes in a similar manner, as long at they implement the same interface.


IShapeFunction[] myShapes = {myCircle, myRectangle};

MessageBox.Show(myShapes[0].GetArea().ToString());


This code creates an array that can contain any class that implements the IShapeFunction interface. Its only interest is in using the GetArea method, and it neither has nor requires any knowledge of other class members. We can easily extend this example to create a class to work with this array that has no knowledge of the Circle or Rectangle class.


public class ShapeUtil

{

   public static double SumAreas

         (IShapeFunction[] funArray)

   {

      double tot = 0.0;

      for (int i = 0; i < funArray.Length; i++)

         { tot += funArray[i].GetArea();  }

      return tot;

   }

}

// Access this method with ShapeUtil.SumAreas(myShapes);


The code in this class can be used with any concrete class that implements the IShapeFunction interface.

Core Approach

For maximum program flexibility, consider using interface types, rather than class types, when defining method parameters. This ensures there is no limit on the types of classes that can be passed to the method, as long as they implement the required interface.


Working with Interfaces

Determining Which Interface Members Are Available

You may not always know at compile time whether a class implements a specific interface. This is often the case when working with a collection that contains a number of types. To perform this check at runtime, use the as or is keyword in your code.


// (1)  as keyword to determine if interface is implemented

Circle myCircle = new Circle(5.0);

IShapeFunction myICircle;

myICircle = myCircle as IShapeFunction;

If (myICircle !=null) //interface is implemented



// (2)  is keyword to determine if interface is implemented

Circle myCircle = new Circle(5.0);

If (myCircle is IShapeFunction) // True if interface implemented


Accessing Interface Methods

Because a class may inherit methods from a base class and/or multiple interfaces, there is the possibility that inherited methods will have the same name. To avoid this ambiguity, specify an interface method declaration in the derived class with the interface and method name:


double IShapeFunction.GetArea() {  // <interface>.<method>


This not only permits a class to implement multiple methods, but has the added effect of limiting access to this method to interface references only. For example, the following would result in an error:


Circle myCircle = new Circle(5.0);

// cannot reference explicit method

double myArea = myCircle.GetArea();


Interfaces Versus Abstract Classes

In the overall design of a system, the real issue is not whether to use a class or interface, but how to best mix the two. The C# restriction on multiple implementation inheritance assures an expanded role for interfaces. It is just too burdensome to try to load all the needed functionality into one base class or a hierarchy of base classes.

A better solution is to define a base class and then expand its capabilities by adding corresponding interfaces. This permits developers to use the class directly without regard to whether the methods are interface implementations. Yet, it also permits the developer to make use of the interface and ignore the other class members.

Of course, you must consider the drawbacks to using interfaces. A well-designed base class reduces the burden on the inherited class to implement everything by promoting code reuse. Also, a non-abstract class can add a member without breaking any existing derived class; adding a member to an interface would break any class implementing the interface.

    Previous Section  < Day Day Up >  Next Section