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.
|