attributes
?
access-modifier
?
|
new?
|
interface
interface-name
[ : base-interface
+
]?
|
{
interface-members
}
|
An interface is like a class, but with these 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; a class can inherit only from a single class.
A struct can implement an interface; a struct can’t inherit from a class.
In Section 2.9, 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 comprises one or more methods, properties, indexers, and events. These members are always implicitly public and implicitly abstract (therefore virtual and nonstatic).
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’s a simple interface that defines a single method:
public interface IDelete { void Delete( ); }
Classes
or
structs that implement an interface may be said to “fulfill the
contract of the interface.” In this example, GUI controls that
support the concept of deleting, such as a TextBox
or TreeView
, or your own custom GUI control, can
implement the IDelete
interface:
public class TextBox : IDelete { public void Delete( ) {...} } public class TreeView : IDelete { public void Delete( ) {...} }
If a class inherits from a base class, the name of each interface to be implemented must appear after the base class name:
public class TextBox : Control, IDelete {...} public class TreeView : Control, IDelete {...}
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, where the currently focused
control is accessed with the ActiveControl
property. When a user clicks Delete
on a menu item
or a toolbar button, you test to see if
ActiveControl
implements
IDelete
, and if so, cast it to
IDelete
to call its Delete
method:
class MyForm { ... void DeleteClick( ) { if (ActiveControl is IDelete) { IDelete d = (IDelete)ActiveControl; d.Delete( ); } } }
Interfaces may extend other interfaces. For instance:
ISuperDelete : IDelete { bool CanDelete {get;} event EventHandler CanDeleteChanged; }
In implementing the ISuperDelete
interface, an
ActiveControl
implements the
CanDelete
property to indicate it has something to
delete and isn’t read-only. The control also implements the
CanDeleteChanged
event to fire an event whenever
its CanDelete
property changes. This framework
lets the application ghost its Delete menu item and toolbar button
when the ActiveControl
is unable to delete.
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 that arises when we
implement two interfaces that each 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, they are implicitly public
, while an
implicit implementation requires the use of the
public
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( );
If a
base
class implements an interface member with the
virtual
(or abstract
) modifier,
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( ) {} }
The implementation in this example lets you use a
RichTextBox
object as an
IDelete
object, and to call
RichTextBox
’s version of
Delete
.
A class or struct T can be implicitly cast to an interface I that T implements. Similarly, an interface X can be implicitly cast to an interface Y that X inherits from. An interface can 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 T can 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.
3.145.173.199