Previous Page
Next Page

Creating Interfaces

Inheriting from a class is a powerful mechanism, but the real power of inheritance comes from inheriting from an interface. An interface allows you to completely separate the name of a method from its implementation.

For example, suppose you want to define a new collection class that allows you to store objects in a particular sequence. When you define the collection class, you do not want to restrict the types of object that it can hold, but you need to provide a way of sorting the objects into the specified sequence. The question is, how do you provide a method that sorts objects whose types you do not know when you write the collection class? At first glance, this problem seems similar to the ToString problem described earlier, and can be resolved by declaring a virtual method that other classes override. However, this is not the case. There is not necessarily any form of inheritance relationship between the collection class and the objects that it holds, so a virtual method would not be of much use. The solution is to specify that all objects in the collection must provide a method allowing them to be compared to each other, such as the CompareTo method shown below:

int CompareTo(object obj)
{
    // return 0 if this instance is equal to obj
    // return < 0 if this instance is less than obj
    // return > 0 if this instance is greater than obj
    ...
}

The collection class can then make use of this method to sort its contents.

You can define an interface that includes this method, and specify that the collection class will allow only classes that implement this interface as its contents. This mechanism guarantees that you will be able to call the CompareTo method on all objects in the collection and sort them.

Interfaces allow you to truly separate the “what” from the “how.” The interface tells you only what the name, return type, and parameters of the method are. Exactly how the method is implemented is not a concern of the interface. The interface represents how you want an object to be used, rather than how it happens to be implemented at a particular moment in time.

Interface Syntax

To declare an interface, you use the interface keyword instead of the class or struct keyword. Inside the interface, you declare methods exactly as in a class or a struct, except that you never specify an access modifier (no public, private, or protected access), and you replace the method body with a semicolon. Here is an example:

interface IComparable
{
    int CompareTo(object obj);
}
TIP
The Microsoft .NET Framework documentation recommends that you preface the name of your interfaces with a capital I. This convention is the last vestige of Hungarian Notation in C#. Incidentally, the System namespace already defines the IComparable interface as shown here.
Interface Restrictions

The essential idea to remember is that an interface never contains any implementation. The following restrictions are natural consequences of this:

Implementing an Interface

To implement an interface, you declare a class or struct that inherits from the interface and implements all the interface methods. For example, suppose you are defining the Token hierarchy shown earlier, but need to specify that all classes in the hierarchy provide a method called Name that returns the name of the current token as a string. You could define the IToken interface that contains this method:

interface IToken
{
    string Name();
}

You could the implement this interface in the Token class:

class Token : IToken
{
    ...
    string IToken.Name() 
    {
        ...
    }
}

When you implement an interface, you must ensure that each method matches its corresponding interface method exactly, according to the following guidelines:

If there is any difference between the interface definition and its declared implementation, the class will not compile.

A class can extend another class and implement an interface at the same time. In this case, C# does not distinguish between the base class and the interface by using keywords as, for example, Java does. Instead, C# uses a positional notation. The base class is named first, followed by a comma, followed by the interface. For example:

interface IToken
{
    ...
}

class DefaultTokenImpl 
{
    ...
}

class IdentifierToken : DefaultTokenImpl , IToken
{
    ...
}
Referencing a Class Through Its Interface

In the same way that you can reference an object by using a variable defined as a class that is higher up the hierarchy, you can reference an object by using a variable defined as an interface that its class implements. Taking the previous example, you can reference an IdentifierToken object by using an IToken variable, as follows:

IdentifierToken it = new IdentifierToken();
IToken iTok = it; // legal

This technique is useful because it allows you to define methods that can take different types as parameters, as long as the types implement a specified interface. For example, the Process method shown below can take any argument that implements the IToken interface:

void Process(Itoken iTok)
{
    ...
}

Note that you can invoke only methods that are visible through the interface when referencing an object in this way.

Working with Multiple Interfaces

A class can have at most one base class, but is allowed to implement an unlimited number of interfaces. A class must still implement all the methods it inherits from all its interfaces. An interface is not allowed to inherit from any kind of class, but an interface is allowed to inherit other interfaces.

If an interface, struct, or class inherits from more than one interface, you write the interfaces in a comma-separated list. If a class also has a base class, the interfaces are listed after the base class. For example:

class IdentifierToken : DefaultTokenImpl, IToken, IVisitable
{
    ...
}

Previous Page
Next Page