An interface is similar to a class, but it provides a specification rather than an implementation for its members. An interface is special in the following ways:
A class can implement multiple interfaces. In contrast, a class can inherit from only a single class.
Interface members are all implicitly abstract. In contrast, a class can provide both abstract members and concrete members with implementations.
Structs can implement interfaces. In contrast, a struct cannot inherit from a class.
An interface declaration is like a class declaration, but it provides no implementation for its members, as all its members are implicitly abstract. These members will be implemented by the classes and structs that implement the interface. An interface can contain only methods, properties, events, and indexers, which noncoincidentally are precisely the members of a class that can be abstract.
Here is a slightly simplified version of the IEnumerator
interface, defined in System.Collections
:
public interface
IEnumerator
{
bool MoveNext();
object Current {get;}
}
Interface members have the same accessibility as the interface type, and they cannot declare an access modifier.
Implementing an interface means providing a public
implementation for all its members:
internal class Countdown : IEnumerator { int count = 11; public bool MoveNext() { return count-- > 0 ; } public object Current { get { return count; } } }
You can implicitly cast an object to any interface that it implements. For example:
IEnumerator e = new Countdown(); while (e.MoveNext()) Console.Write (e.Current); // 109876543210
Even though Countdown
is an internal class, its
members that implement IEnumerator
can be called
publicly by casting an instance of Countdown
to
IEnumerator
.If IEnumerator
was itself internal
instead of
public
, this wouldn’t be possible.
Interfaces may derive from other interfaces. For instance:
public interface IUndoable { void Undo(); } public interface IRedoable : IUndoable { void Redo(); }
IRedoable
inherits all the members of IUndoable
.
Implementing multiple interfaces can sometimes result in a collision between member signatures. You can resolve such collisions by explicitly implementing an interface member. Consider the following example:
interface I1 { void Foo(); }
interface I2 { int Foo(); }
public class Widget : I1, I2
{
public void Foo()
{
Console.WriteLine
("Widget's implementation of I1.Foo");
}
int I2.Foo()
{
Console.WriteLine
("Widget's implementation of I2.Foo");
return 42;
}
}
Because both I1
and I2
have conflicting Foo
signatures,
Widget
explicitly implements I2's Foo
method. This lets the two methods coexist in one class. The only way
to call an explicitly implemented member is to cast to its interface:
Widget w = new Widget(); w.Foo(); // Widget's implementation of I1.Foo ((I1)w).Foo(); // Widget's implementation of I1.Foo ((I2)w).Foo(); // Widget's implementation of I2.Foo
Another reason to explicitly implement interface members is to hide members that are
highly specialized and distracting to a type’s normal use case. For example, a type that
implements ISerializable
would typically want to avoid
flaunting its ISerializable
members unless explicitly
cast to that interface.
An implicitly implemented interface member is, by default, sealed. It must be marked
virtual
or abstract
in the base class to be overridden. For example:
public interface IUndoable { void Undo(); } public class TextBox : IUndoable { publicvirtual
void Undo() { Console.WriteLine ("TextBox.Undo"); } } public class RichTextBox : TextBox { publicoverride
void Undo() { Console.WriteLine ("RichTextBox.Undo"); } }
Calling the interface member through either the base class or the interface calls the subclass’s implementation:
RichTextBox r = new RichTextBox(); r.Undo(); // RichTextBox.Undo ((IUndoable)r).Undo(); // RichTextBox.Undo ((TextBox)r).Undo(); // RichTextBox.Undo
An explicitly implemented interface member cannot be marked virtual
, nor can it be overridden in the usual manner. It can, however, be
reimplemented.
A subclass can reimplement any interface member already implemented by a base class.
Reimplementation hijacks a member implementation (when called through the interface) and
works whether or not the member is virtual
in the base
class. It also works whether a member is implemented implicitly or explicitly—although it
works best in the latter case, as we will demonstrate.
In the following example, TextBox
explicitly
implements IUndo.Undo
, so it cannot be marked as
virtual
. To “override” it, RichTextBox
must reimplement IUndo's Undo
method:
public interface IUndoable { void Undo(); }
public class TextBox : IUndoable
{
void IUndoable.Undo()
{ Console.WriteLine ("TextBox.Undo"); }
}
public class RichTextBox : TextBox, IUndoable
{
public new void Undo()
{ Console.WriteLine ("RichTextBox.Undo"); }
}
Calling the reimplemented member through the interface calls the subclass’s implementation:
RichTextBox r = new RichTextBox(); r.Undo(); // RichTextBox.Undo Case 1 ((IUndoable)r).Undo(); // RichTextBox.Undo Case 2
Assuming the same RichTextBox
definition, suppose
now that TextBox
implemented Undo
implicitly:
public class TextBox : IUndoable { public void Undo() { Console.WriteLine ("TextBox.Undo"); } }
This would give us another way to call Undo
, which
would “break” the system, as shown in Case 3:
RichTextBox r = new RichTextBox();
r.Undo(); // RichTextBox.Undo Case 1
((IUndoable)r).Undo(); // RichTextBox.Undo Case 2
((TextBox)r).Undo(); // TextBox.Undo
Case 3
Case 3 demonstrates that reimplementation hijacking is effective only when a member is called through the interface, and not through the base class. This is usually undesirable as it can mean inconsistent semantics, making reimplementation most appropriate for overriding explicitly implemented interface members.