DekGenius.com
[ Team LiB ] Previous Section Next Section

3.5 Interfaces

attributes? unsafe? access-modifier?
new?
interface interface-name 
[ : base-interface+ ]?
{ interface-members }

An interface is similar to a class, but with the following major differences:

  • An interface provides a specification rather than an implementation for its members. This is similar to a pure abstract class, which is an abstract class consisting of only abstract members.

  • A class and struct can implement multiple interfaces, while a class can inherit only from a single class.

  • A struct can implement an interface, but a struct cannot inherit from a class.

Earlier in this chapter, we defined polymorphism as the ability to perform the same operations on many types, as long as each type shares a common subset of characteristics. The purpose of an interface is precisely for defining such a set of characteristics.

An interface is comprised of a set of the following members:

  • Method

  • Property

  • Indexer

  • Event

These members are always implicitly public and implicitly abstract (and therefore virtual and nonstatic).

3.5.1 Defining an Interface

An interface declaration is like a class declaration, but it provides no implementation for its members, since all its members are implicitly abstract. These members are intended to be implemented by a class or struct that implements the interface. Here is a very simple interface that defines a single method:

public interface IDelete {
   void Delete( );
}

3.5.2 Implementing an Interface

Classes or structs that implement an interface may be said to "fulfill the contract of the interface." In this example, our IDelete interface can be implemented by GUI controls that support the concept of deleting, such as a TextBox, TreeView, or your own custom GUI control.

public class TextBox : IDelete {
  public void Delete( ) {...}
}
public class TreeView : IDelete {
  public void Delete( ) {...}
}

If a class inherits from a base class, then each interface implemented must appear after the base class:

public class TextBox : Control, IDelete {...}
public class TreeView : Control, IDelete {...}

3.5.3 Using an Interface

An interface is useful when you need multiple classes to share characteristics not present in a common base class. In addition, an interface is a good way to ensure that these classes provide their own implementation for the interface member, since interface members are implicitly abstract.

The following example assumes a form containing many GUI controls (including some TextBox and TreeView controls) in which the currently focused control is accessed with the ActiveControl property. When a user clicks Delete on a menu item or toolbar button, the example tests to see whether ActiveControl implements IDelete, and if so, casts it to IDelete to call its Delete method:

class MyForm {
  ...
  void DeleteClick( ) {
    if (ActiveControl is IDelete)
      PerformDelete ((IDelete)ActiveControl);
  }
}

3.5.4 Extending an Interface

Interfaces may extend other interfaces. For instance:

ISuperDelete : IDelete {
  bool CanDelete {get;}
  event EventHandler CanDeleteChanged;
}

A control implements the CanDelete property to indicate that it has something to delete and is not read-only, and implements the CanDeleteChanged event to fire an event whenever its CanDelete property changes. This framework allows our application to ghost its Delete menu item and toolbar button when the ActiveControl is unable to delete.

3.5.5 Explicit Interface Implementation

If there is a name collision between an interface member and an existing member in the class or struct, C# allows you to explicitly implement an interface member to resolve the conflict. In this example, we resolve a conflict when implementing two interfaces that both define a Delete method:

public interface IDesignTimeControl {
   ...
   object Delete( );
}
public class TextBox : IDelete, IDesignTimeControl {
   ...
   void IDelete.Delete( ) {  }
   object IDesignTimeControl.Delete( ) {...}
   // Note that explicitly implementing just one of them would
   // be enough to resolve the conflict
}

Unlike implicit interface implementations, explicit interface implementations can't be declared with abstract, virtual, override, or new modifiers. In addition, while an implicit implementation requires the use of the public modifier, an explicit implementation has no access modifier. However, to access the method, the class or struct must be cast to the appropriate interface first:

TextBox tb = new TextBox( );
IDesignTimeControl idtc = (IDesignTimeControl)tb;
IDelete id = (IDelete)tb;
idtc.Delete( );
id.Delete( );

3.5.6 Reimplementing an Interface

If a base class implements an interface member with the virtual (or abstract) modifier, then a derived class can override it. If not, the derived class must reimplement the interface to override that member:

public class RichTextBox : TextBox, IDelete {
   // TextBox's IDelete.Delete is not virtual (since explicit
   // interface implementations cannot be virtual)
   public void Delete( ) {  }
}

This lets us use a RichTextBox as an IDelete, and calls RichTextBox's version of Delete.

3.5.7 Interface Conversions

A class or struct T may be implicitly cast to an interface I that T implements. Similarly, an interface X may be implicitly cast to an interface Y that X inherits from. An interface may be explicitly cast to any other interface or nonsealed class. However, an explicit cast from an interface I to a sealed class or struct T is permitted only if it is possible that T could implement I. For example:

interface IDelete {...}
interface IDesignTimeControl {...}
class TextBox : IDelete, IDesignTimeControl {...}
sealed class Timer : IDesignTimeControl {...}
  
TextBox tb1 = new TextBox ( );
IDelete d = tb1; // implicit cast
IDesignTimeControl dtc = (IDesignTimeControl)d;
TextBox tb2 = (TextBox)dtc;
Timer t = (Timer)d; // illegal, a Timer can never implement IDelete

Standard boxing conversions happen when converting between structs and interfaces.

    [ Team LiB ] Previous Section Next Section