ECMA-334: 20.4.2 Interface mapping
| C# Language Specification |
| © 2006 ECMA International |
20.4.2 Interface mapping
A class or struct shall provide implementations of all members of the interfaces that are listed in the base class list of the class or struct. The process of locating implementations of interface members in an implementing class or struct is known as interface mapping.
Interface mapping for a class or struct C locates an implementation for each member of each interface
specified in the base class list of C. The implementation of a particular interface member I.M, where I is the
interface in which the member M is declared, is determined by examining each class or struct S, starting with
C and repeating for each successive base class of C, until a match is located:
- If
Scontains a declaration of an explicit interface member implementation that matchesIandM, then this member is the implementation ofI.M.
- If
- Otherwise, if
Scontains a declaration of a non-static public member that matchesM, then this member is the implementation ofI.M.
- Otherwise, if
A compile-time error occurs if implementations cannot be located for all members of all interfaces specified
in the base class list of C. The members of an interface include those members that are inherited from base
interfaces.
Members of a constructed interface type are considered to have any type parameters replaced with the corresponding type arguments as specified in §25.5.4. [Example: For example, given the generic interface declaration:
interface I<T> { T F(int x, T[,] y); T this[int y] { get; } }
the constructed interface I<string[]> has the members:
string[] F(int x, string[,][] y); string[] this[int y] { get; }
end example]
For purposes of interface mapping, a class member A matches an interface member B when:
-
AandBare methods, and the name, type, and formal parameter lists ofAandBare identical.
-
-
AandBare properties, the name and type ofAandBare identical, andAhas the same accessors asB(Ais permitted to have additional accessors if it is not an explicit interface member implementation).
-
-
AandBare events, and the name and type ofAandBare identical.
-
-
AandBare indexers, the type and formal parameter lists ofAandBare identical, andAhas the same accessors asB(Ais permitted to have additional accessors if it is not an explicit interface member implementation).
-
Notable implications of the interface-mapping algorithm are:
- Explicit interface member implementations take precedence over other members in the same class or struct when determining the class or struct member that implements an interface member.
- Neither non-public nor static members participate in interface mapping.
[Example: In the following code
interface ICloneable { object Clone(); } class C: ICloneable { object ICloneable.Clone() {…} public object Clone() {…} }
the ICloneable.Clone member of C becomes the implementation of Clone in ICloneable because
explicit interface member implementations take precedence over other members. end example]
If a class or struct implements two or more interfaces containing a member with the same name, type, and parameter types, it is possible to map each of those interface members onto a single class or struct member. [Example:
interface IControl { void Paint(); } interface IForm { void Paint(); } class Page: IControl, IForm { public void Paint() {…} }
Here, the Paint methods of both IControl and IForm are mapped onto the Paint method in Page. It is
of course also possible to have separate explicit interface member implementations for the two methods. end example]
If a class or struct implements an interface that contains hidden members, then some members shall necessarily be implemented through explicit interface member implementations. [Example:
interface IBase { int P { get; } } interface IDerived: IBase { new int P(); }
An implementation of this interface would require at least one explicit interface member implementation, and would take one of the following forms
class C: IDerived { int IBase.P { get {…} } int IDerived.P() {…} } class C: IDerived { public int P { get {…} } int IDerived.P() {…} } class C: IDerived { int IBase.P { get {…} } public int P() {…} }
end example]
When a class implements multiple interfaces that have the same base interface, there can be only one implementation of the base interface. [Example: In the following code
interface IControl { void Paint(); } interface ITextBox: IControl { void SetText(string text); } interface IListBox: IControl { void SetItems(string[] items); } class ComboBox: IControl, ITextBox, IListBox { void IControl.Paint() {…} void ITextBox.SetText(string text) {…} void IListBox.SetItems(string[] items) {…} }
it is not possible to have separate implementations for the IControl named in the base class list, the
IControl inherited by ITextBox, and the IControl inherited by IListBox. Indeed, there is no notion of
a separate identity for these interfaces. Rather, the implementations of ITextBox and IListBox share the
same implementation of IControl, and ComboBox is simply considered to implement three interfaces,
IControl, ITextBox, and IListBox. end example]
The members of a base class participate in interface mapping. [Example: In the following code
interface Interface1 { void F(); } class Class1 { public void F() {} public void G() {} } class Class2: Class1, Interface1 { new public void G() {} }
the method F in Class1 is used in Class2's implementation of Interface1. end example]