only for RuBoard - do not distribute or recompile |
Syntax: |
---|
attributes? unsafe? 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 but can't inherit from a class.
Earlier 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 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, in which 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:
interface 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 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.
only for RuBoard - do not distribute or recompile |