Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
19.1 General
An interface defines a contract. A class or struct that implements an interface shall adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces.
Interfaces may contain various kinds of members, as described in §19.4. The interface itself may provide an implementation for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations must be supplied by classes or structs that implement the interface, or derived interface that provide an overriding definition.
Note: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change. The addition of interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the implementation as a non-breaking change; however, if their requirements are different, they can override the provided implementations. end note
19.2 Interface declarations
19.2.1 General
An interface_declaration is a type_declaration (§14.7) that declares a new interface type.
interface_declaration
: attributes? interface_modifier* 'partial'? 'interface'
identifier variant_type_parameter_list? interface_base?
type_parameter_constraints_clause* interface_body ';'?
;
An interface_declaration consists of an optional set of attributes (§23), followed by an optional set of interface_modifiers (§19.2.2), followed by an optional partial modifier (§15.2.7), followed by the keyword interface and an identifier that names the interface, followed by an optional variant_type_parameter_list specification (§19.2.3), followed by an optional interface_base specification (§19.2.4), followed by an optional type_parameter_constraints_clauses specification (§15.2.5), followed by an interface_body (§19.3), optionally followed by a semicolon.
An interface declaration shall not supply type_parameter_constraints_clauses unless it also supplies a variant_type_parameter_list.
An interface declaration that supplies a variant_type_parameter_list is a generic interface declaration. Additionally, any interface nested inside a generic class declaration or a generic struct declaration is itself a generic interface declaration, since type arguments for the containing type shall be supplied to create a constructed type (§8.4).
19.2.2 Interface modifiers
An interface_declaration may optionally include a sequence of interface modifiers:
interface_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§24.2) is only available in unsafe code (§24).
It is a compile-time error for the same modifier to appear multiple times in an interface declaration.
The new modifier is only permitted on interfaces defined within a class. It specifies that the interface hides an inherited member by the same name, as described in §15.3.5.
The public, protected, internal, and private modifiers control the accessibility of the interface. Depending on the context in which the interface declaration occurs, only some of these modifiers might be permitted (§7.5.2). When a partial type declaration (§15.2.7) includes an accessibility specification (via the public, protected, internal, and private modifiers), the rules in §15.2.2 apply.
19.2.3 Variant type parameter lists
19.2.3.1 General
Variant type parameter lists can only occur on interface and delegate types. The difference from ordinary type_parameter_lists is the optional variance_annotation on each type parameter.
variant_type_parameter_list
: '<' variant_type_parameter (',' variant_type_parameter)* '>'
;
variant_type_parameter
: attributes? variance_annotation? type_parameter
;
variance_annotation
: 'in'
| 'out'
;
If the variance annotation is out, the type parameter is said to be covariant. If the variance annotation is in, the type parameter is said to be contravariant. If there is no variance annotation, the type parameter is said to be invariant.
Example: In the following:
interface C<out X, in Y, Z> { X M(Y y); Z P { get; set; } }
Xis covariant,Yis contravariant andZis invariant.end example
If a generic interface is declared in multiple parts (§15.2.3), each partial declaration shall specify the same variance for each type parameter.
19.2.3.2 Variance safety
The occurrence of variance annotations in the type parameter list of a type restricts the places where types can occur within the type declaration.
A type T is output-unsafe if one of the following holds:
Tis a contravariant type parameterTis an array type with an output-unsafe element typeTis an interface or delegate typeSᵢ,... Aₑconstructed from a generic typeS<Xᵢ, ... Xₑ>where for at least oneAᵢone of the following holds:Xᵢis covariant or invariant andAᵢis output-unsafe.Xᵢis contravariant or invariant andAᵢis input-unsafe.
A type T is input-unsafe if one of the following holds:
Tis a covariant type parameterTis an array type with an input-unsafe element typeTis an interface or delegate typeS<Aᵢ,... Aₑ>constructed from a generic typeS<Xᵢ, ... Xₑ>where for at least oneAᵢone of the following holds:Xᵢis covariant or invariant andAᵢis input-unsafe.Xᵢis contravariant or invariant andAᵢis output-unsafe.
Intuitively, an output-unsafe type is prohibited in an output position, and an input-unsafe type is prohibited in an input position.
A type is output-safe if it is not output-unsafe, and input-safe if it is not input-unsafe.
19.2.3.3 Variance conversion
The purpose of variance annotations is to provide for more lenient (but still type safe) conversions to interface and delegate types. To this end the definitions of implicit (§10.2) and explicit conversions (§10.3) make use of the notion of variance-convertibility, which is defined as follows:
A type T<Aᵢ, ..., Aᵥ> is variance-convertible to a type T<Bᵢ, ..., Bᵥ> if T is either an interface or a delegate type declared with the variant type parameters T<Xᵢ, ..., Xᵥ>, and for each variant type parameter Xᵢ one of the following holds:
Xᵢis covariant and an implicit reference or identity conversion exists fromAᵢtoBᵢXᵢis contravariant and an implicit reference or identity conversion exists fromBᵢtoAᵢXᵢis invariant and an identity conversion exists fromAᵢtoBᵢ
19.2.4 Base interfaces
An interface can inherit from zero or more interface types, which are called the explicit base interfaces of the interface. When an interface has one or more explicit base interfaces, then in the declaration of that interface, the interface identifier is followed by a colon and a comma-separated list of base interface types.
A derived interface may declare new members that hide inherited members (§7.7.2.3) declared in base interfaces or explicitly implement inherited members (§19.6.2) declared in base interfaces.
interface_base
: ':' interface_type_list
;
The explicit base interfaces can be constructed interface types (§8.4, §19.2). A base interface cannot be a type parameter on its own, though it can involve the type parameters that are in scope.
For a constructed interface type, the explicit base interfaces are formed by taking the explicit base interface declarations on the generic type declaration, and substituting, for each type_parameter in the base interface declaration, the corresponding type_argument of the constructed type.
The explicit base interfaces of an interface shall be at least as accessible as the interface itself (§7.5.5).
Note: For example, it is a compile-time error to specify a
privateorinternalinterface in the interface_base of apublicinterface. end note
It is a compile-time error for an interface to directly or indirectly inherit from itself.
The base interfaces of an interface are the explicit base interfaces and their base interfaces. In other words, the set of base interfaces is the complete transitive closure of the explicit base interfaces, their explicit base interfaces, and so on. An interface inherits all members of its base interfaces.
Example: In the following code
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } interface IListBox : IControl { void SetItems(string[] items); } interface IComboBox: ITextBox, IListBox {}the base interfaces of
IComboBoxareIControl,ITextBox, andIListBox. In other words, theIComboBoxinterface above inherits membersSetTextandSetItemsas well asPaint.end example
Members inherited from a constructed generic type are inherited after type substitution. That is, any constituent types in the member have the base class declaration’s type parameters replaced with the corresponding type arguments used in the class_base specification.
Example: In the following code
interface IBase<T> { T[] Combine(T a, T b); } interface IDerived : IBase<string[,]> { // Inherited: string[][,] Combine(string[,] a, string[,] b); }the interface
IDerivedinherits theCombinemethod after the type parameterTis replaced withstring[,].end example
A class or struct that implements an interface also implicitly implements all of the interface’s base interfaces.
The handling of interfaces on multiple parts of a partial interface declaration (§15.2.7) are discussed further in §15.2.4.3.
Every base interface of an interface shall be output-safe (§19.2.3.2).
19.3 Interface body
The interface_body of an interface defines the members of the interface.
interface_body
: '{' interface_member_declaration* '}'
;
19.4 Interface members
19.4.1 General
The members of an interface are the members inherited from the base interfaces and the members declared by the interface itself.
interface_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| static_constructor_declaration
| operator_declaration
| type_declaration
;
This clause augments the description of members in classes (§15.3) with restrictions for interfaces. The Interface members are declared using member_declarations with the following additional rules:
- A finalizer_declaration is not allowed.
- Instance constructors, constructor_declarations, are not allowed.
- All interface members implicitly have public access; however, an explicit access modifier (§7.5.2) is permitted except on static constructors (§15.12).
- The
abstractmodifier is implied for interface function members without bodies; that modifier may be given explicitly. - An interface instance function member whose declaration includes a body is an implicitly
virtualmember unless thesealedorprivatemodifier is used. Thevirtualmodifier may be given explicitly. - A
privateorsealedfunction member of an interface shall have a body. - A
privatefunction member shall not have the modifiersealed. - A derived interface may override an abstract or virtual member declared in a base interface.
- An explicitly implemented function member shall not have the modifier
sealed.
Some declarations, such as constant_declaration (§15.4) have no restrictions in interfaces.
The inherited members of an interface are specifically not part of the declaration space of the interface. Thus, an interface is allowed to declare a member with the same name or signature as an inherited member. When this occurs, the derived interface member is said to hide the base interface member. Hiding an inherited member is not considered an error, but it does result in a warning (§7.7.2.3).
If a new modifier is included in a declaration that doesn’t hide an inherited member, a warning is issued to that effect.
Note: The members in class
objectare not, strictly speaking, members of any interface (§19.4). However, the members in classobjectare available via member lookup in any interface type (§12.5). end note
The set of members of an interface declared in multiple parts (§15.2.7) is the union of the members declared in each part. The bodies of all parts of the interface declaration share the same declaration space (§7.3), and the scope of each member (§7.7) extends to the bodies of all the parts.
Example: Consider an interface
IAwith an implementation for a memberMand a propertyP. An implementing typeCdoesn’t provide an implementation for eitherMorP. They must be accessed through a reference whose compile-time type is an interface that is implicitly convertible toIAorIB. These members aren’t found through member lookup on a variable of typeC.interface IA { public int P { get { return 10; } } public void M() { Console.WriteLine("IA.M"); } } interface IB : IA { public new int P { get { return 20; } } void IA.M() { Console.WriteLine("IB.M"); } } class C : IB { } class Test { public static void Main() { C c = new C(); ((IA)c).M(); // cast needed Console.WriteLine($"IA.P = {((IA)c).P}"); // cast needed Console.WriteLine($"IB.P = {((IB)c).P}"); // cast needed } }Within the interfaces
IAandIB, memberMis accessible directly by name. However, within methodMain, we cannot writec.M()orc.P, as those names are not visible. To find them, casts to the appropriate interface type are needed. The declaration ofMinIBuses explicit interface implementation syntax. This is necessary to make that method override the one inIA; the modifieroverridemay not be applied to a function member. end example
19.4.2 Interface fields
This clause augments the description of fields in classes §15.5 for fields declared in interfaces.
Interface fields are declared using field_declarations (§15.5.1) with the following additional rules:
- It is a compile-time error for field_declaration to declare an instance field.
Example: The following program contains static members of various kinds:
public interface IX { public const int Constant = 100; protected static int field; static IX() { Console.WriteLine("static members initialized"); Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}"); field = 50; Console.WriteLine("static constructor has run"); } } public class Test: IX { public static void Main() { Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}"); } }The output produced is
static members initialized constant = 100, field = 0 static constructor has run constant = 100, field = 50end example
See §19.4.8 for information regarding the allocation and initialization of static fields.
19.4.3 Interface methods
This clause augments the description of methods in classes §15.6 for methods declared in interfaces.
Interface methods are declared using method_declarations (§15.6)). The attributes, return_type, ref_return_type, identifier, and parameter_list of an interface method declaration have the same meaning as those of a method declaration in a class. Interface methods have the following additional rules:
method_modifier shall not include
override.A method whose body is a semi-colon (
;) isabstract; theabstractmodifier is not required, but is allowed.An interface method declaration that has a block body or expression body as a method_body is
virtual; thevirtualmodifier is not required, but is allowed.A method_declaration shall not have type_parameter_constraints_clauses unless it also has a type_parameter_list.
The list of requirements for valid combinations of modifiers stated for a class method is extended, as follows:
- A static declaration that is not extern shall have a block body or expression body as a method_body.
- A virtual declaration that is not extern shall have a block body or expression body as a method_body.
- A private declaration that is not extern shall have a block body or expression body as a method_body.
- A sealed declaration that is not extern shall have a block body or expression body as a method_body.
- An async declaration shall have a block body or expression body as a method_body.
All parameter types of an interface method shall be input-safe (§19.2.3.2), and the return type shall be either
voidor output-safe.Any output or reference parameter types shall also be output-safe.
Note: Output parameters are required to be input-safe due to common implementation restrictions. end note
Each class type constraint, interface type constraint and type parameter constraint on any type parameters of the method shall be input-safe.
These rules ensure that any covariant or contravariant usage of the interface remains typesafe.
Example:
interface I<out T> { void M<U>() where U : T; // Error }is ill-formed because the usage of
Tas a type parameter constraint onUis not input-safe.Were this restriction not in place it would be possible to violate type safety in the following manner:
interface I<out T> { void M<U>() where U : T; } class B {} class D : B {} class E : B {} class C : I<D> { public void M<D>() {...} } ... I<B> b = new C(); b.M<E>();This is actually a call to
C.M<E>. But that call requires thatEderive fromD, so type safety would be violated here.end example
Note: See §19.4.2 for an example that not only shows a static method with an implementation, but as that method is called
Mainand has the right return type and signature, it’s also an entry point. end note
A virtual method with implementation declared in an interface may be overridden to be abstract in a derived interface. This is known as reabstraction.
Example:
interface IA { void M() { Console.WriteLine("IA.M"); } } interface IB: IA { abstract void IA.M(); // reabstraction of M }This is useful in derived interfaces where the implementation of a method is inappropriate and a more appropriate implementation should be provided by implementing classes. end example
19.4.4 Interface properties
This clause augments the description of properties in classes §15.7 for properties declared in interfaces.
Interface properties are declared using property_declarations (§15.7.1) with the following additional rules:
property_modifier shall not include
override.An explicit interface member implementation shall not contain an accessor_modifier (§15.7.3).
A derived interface may explicitly implement an abstract interface property declared in a base interface.
Note: As an interface cannot contain instance fields, an interface property cannot be an instance auto-property, as that would require the declaration of implicit hidden instance fields. end note
The type of an interface property shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor.
An interface method declaration that has a block body or expression body as a method_body is
virtual; thevirtualmodifier is not required, but is allowed.An instance property_declaration that has no implementation is
abstract; theabstractmodifier is not required, but is allowed. It is never considered to be an automatically implemented property (§15.7.4).
19.4.5 Interface events
This clause augments the description of events in classes §15.8 for events declared in interfaces.
Interface events are declared using event_declarations (§15.8.1), with the following additional rules:
- event_modifier shall not include
override. - A derived interface may implement an abstract interface event declared in a base interface (§15.8.5).
- It is a compile-time error for variable_declarators in an instance event_declaration to contain any variable_initializers.
- An instance event with the
virtualorsealedmodifiers must declare accessors. It is never considered to be an automatically implemented field-like event (§15.8.2). - An instance event with the
abstractmodifier must not declare accessors. - The type of an interface event shall be input-safe.
19.4.6 Interface indexers
This clause augments the description of indexers in classes §15.9 for indexers declared in interfaces.
Interface indexers are declared using indexer_declarations (§15.9), with the following additional rules:
indexer_modifier shall not include
override.An indexer_declaration that has an expression body or contains an accessor with a block body or expression body is
virtual; thevirtualmodifier is not required, but is allowed.An indexer_declaration whose accessor bodies are semi-colons (
;) isabstract; theabstractmodifier is not required, but is allowed.All the parameter types of an interface indexer shall be input-safe (§19.2.3.2).
Any output or reference parameter types shall also be output-safe.
Note: Output parameters are required to be input-safe due to common implementation restrictions. end note
The type of an interface indexer shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor.
19.4.7 Interface operators
This clause augments the description of operator_declaration members in classes §15.10 for operators declared in interfaces.
An operator_declaration in an interface is the implementation (§19.1).
It is a compile-time error for an interface to declare a conversion, equality, or inequality operator.
19.4.8 Interface static constructors
This clause augments the description of static constructors in classes §15.12 for static constructors declared in interfaces.
The static constructor for a closed (§8.4.3) interface executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following actions to occur within an application domain:
- Any of the static members of the interface are referenced.
- Before the
Mainmethod is called for an interface containing theMainmethod (§7.1) in which execution begins. - That interface provides an implementation for a member, and that implementation is accessed as the most specific implementation (§19.4.10) for that member.
Note: In the case where none of the preceding actions take place, the static constructor for an interface may not execute for a program where instances of types that implement the interface are created and used. end note
To initialize a new closed interface type, first a new set of static fields for that particular closed type is created. Each of the static fields is initialized to its default value. Next, the static field initializers are executed for those static fields. Finally, the static constructor is executed.
Note: See §19.4.2 for an example of using various kinds of static members (including a Main method) declared within an interface. end note
19.4.9 Interface nested types
This clause augments the description of nested types in classes §15.3.9 for nested types declared in interfaces.
It is an error to declare a class type, struct type, or enum type within the scope of a type parameter that was declared with a variance_annotation (§19.2.3.1).
Example: The declaration of
Cbelow is an error.interface IOuter<out T> { class C { } // error: class declaration within scope of variant type parameter 'T' }end example
19.4.10 most specific implementation
Every class and struct shall have a most specific implementation for every virtual member declared in all interfaces implemented by that type among the implementations appearing in the type or its direct and indirect interfaces. The most specific implementation is a unique implementation that is more specific than every other implementation.
Note: The most specific implementation rule ensures that an ambiguity arising from diamond interface inheritance is resolved explicitly by the programmer at the point where the conflict occurs. end note
For a type T that is a struct or a class that implements interfaces I2 and I3, where I2 and I3 both derive directly or indirectly from interface I that declares a member M, the most specific implementation of M is:
- If
Tdeclares an implementation ofI.M, that implementation is the most specific implementation. - Otherwise, if
Tis a class and a direct or indirect base class declares an implementation ofI.M, the most derived base class ofTis the most specific implementation. - Otherwise, if
I2andI3are interfaces implemented byTandI3derives fromI2either directly or indirectly,I3.Mis a more specific implementation thanI2.M. - Otherwise, neither
I2.MnorI3.Mare more specific and an error occurs.
Example:
interface IA { void M() { Console.WriteLine("IA.M"); } } interface IB : IA { void IA.M() { Console.WriteLine("IB.M"); } } interface IC: IA { void IA.M() { Console.WriteLine("IC.M"); } } abstract class C: IB, IC { } // error: no most specific implementation for 'IA.M' abstract class D: IA, IB, IC // OK { public abstract void M(); }The most specific implementation rule ensures that a conflict (i.e., an ambiguity arising from diamond inheritance) is resolved explicitly by the programmer at the point where the conflict arises. end example
19.4.11 Interface member access
Interface members are accessed through member access (§12.8.7) and indexer access (§12.8.12.4) expressions of the form I.M and I[A], where I is an interface type, M is a constant, field, method, property, or event of that interface type, and A is an indexer argument list.
In a class D, with direct or indirect base class B, where B directly or indirectly implements interface I and I defines a method M(), the expression base.M() is valid only if base.M() staticly (§12.3) binds to an implementation of M() in a class type.
For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup (§12.5), method invocation (§12.8.10.2), and indexer access (§12.8.12.4) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This subclause shows several examples, some of which lead to ambiguities and others which don’t. In all cases, explicit casts can be used to resolve the ambiguities.
Example: In the following code
interface IList { int Count { get; set; } } interface ICounter { int Count { get; set; } } interface IListCounter : IList, ICounter {} class C { void Test(IListCounter x) { x.Count = 1; // Error ((IList)x).Count = 1; // Ok, invokes IList.Count.set ((ICounter)x).Count = 1; // Ok, invokes ICounter.Count } }the first statement causes a compile-time error because the member lookup (§12.5) of
CountinIListCounteris ambiguous. As illustrated by the example, the ambiguity is resolved by castingxto the appropriate base interface type. Such casts have no run-time costs—they merely consist of viewing the instance as a less derived type at compile-time.end example
Example: In the following code
interface IInteger { void Add(int i); } interface IDouble { void Add(double d); } interface INumber : IInteger, IDouble {} class C { void Test(INumber n) { n.Add(1); // Invokes IInteger.Add n.Add(1.0); // Only IDouble.Add is applicable ((IInteger)n).Add(1); // Only IInteger.Add is a candidate ((IDouble)n).Add(1); // Only IDouble.Add is a candidate } }the invocation
n.Add(1)selectsIInteger.Addby applying overload resolution rules of §12.6.4. Similarly, the invocationn.Add(1.0)selectsIDouble.Add. When explicit casts are inserted, there is only one candidate method, and thus no ambiguity.end example
Example: In the following code
interface IBase { void F(int i); } interface ILeft : IBase { new void F(int i); } interface IRight : IBase { void G(); } interface IDerived : ILeft, IRight {} class A { void Test(IDerived d) { d.F(1); // Invokes ILeft.F ((IBase)d).F(1); // Invokes IBase.F ((ILeft)d).F(1); // Invokes ILeft.F ((IRight)d).F(1); // Invokes IBase.F } }the
IBase.Fmember is hidden by theILeft.Fmember. The invocationd.F(1)thus selectsILeft.F, even thoughIBase.Fappears to not be hidden in the access path that leads throughIRight.The intuitive rule for hiding in multiple-inheritance interfaces is simply this: If a member is hidden in any access path, it is hidden in all access paths. Because the access path from
IDerivedtoILefttoIBasehidesIBase.F, the member is also hidden in the access path fromIDerivedtoIRighttoIBase.end example
19.5 Qualified interface member names
An interface member is sometimes referred to by its qualified interface member name. The qualified name of an interface member consists of the name of the interface in which the member is declared, followed by a dot, followed by the name of the member. The qualified name of a member references the interface in which the member is declared.
Example: Given the declarations
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); }the qualified name of
PaintisIControl.Paintand the qualified name of SetText isITextBox.SetText. In the example above, it is not possible to refer toPaintasITextBox.Paint.end example
When an interface is part of a namespace, a qualified interface member name can include the namespace name.
Example:
namespace System { public interface ICloneable { object Clone(); } }Within the
Systemnamespace, bothICloneable.CloneandSystem.ICloneable.Cloneare qualified interface member names for theClonemethod.end example
19.6 Interface implementations
19.6.1 General
Interfaces may be implemented by classes and structs. To indicate that a class or struct directly implements an interface, the interface is included in the base class list of the class or struct.
A class or struct C that implements an interface I must provide or inherit an implementation for every member declared in I that C can access. Public members of I may be defined in public members of C. Non-public members declared in I that are accessible in C may be defined in C using explicit interface implementation (§19.6.2).
A member in a derived type that satisfies interface mapping (§19.6.5) but does not implement the matching base interface member introduces a new member. This occurs when explicit interface implementation is required to define the interface member.
Example:
interface ICloneable { object Clone(); } interface IComparable { int CompareTo(object other); } class ListEntry : ICloneable, IComparable { public object Clone() {...} public int CompareTo(object other) {...} }end example
A class or struct that directly implements an interface also implicitly implements all of the interface’s base interfaces. This is true even if the class or struct doesn’t explicitly list all base interfaces in the base class list.
Example:
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { public void Paint() {...} public void SetText(string text) {...} }Here, class
TextBoximplements bothIControlandITextBox.end example
When a class C directly implements an interface, all classes derived from C also implement the interface implicitly.
The base interfaces specified in a class declaration can be constructed interface types (§8.4, §19.2).
Example: The following code illustrates how a class can implement constructed interface types:
class C<U, V> {} interface I1<V> {} class D : C<string, int>, I1<string> {} class E<T> : C<int, T>, I1<T> {}end example
The base interfaces of a generic class declaration shall satisfy the uniqueness rule described in §19.6.3.
19.6.2 Explicit interface member implementations
For purposes of implementing interfaces, a class, struct, or interface may declare explicit interface member implementations. An explicit interface member implementation is a method, property, event, or indexer declaration that references a qualified interface member name. A class or struct that implements a non-public member in a base interface must declare an explicit interface member implementation. An interface that implements a member in a base interface must declare an explicit interface member implementation.
A derived interface member that satisfies interface mapping (§19.6.5) hides the base interface member (§7.7.2). The compiler shall issue a warning unless the new modifier is present.
Example:
interface IList<T> { T[] GetElements(); } interface IDictionary<K, V> { V this[K key] { get; } void Add(K key, V value); } class List<T> : IList<T>, IDictionary<int, T> { public T[] GetElements() {...} T IDictionary<int, T>.this[int index] {...} void IDictionary<int, T>.Add(int index, T value) {...} }Here
IDictionary<int,T>.thisandIDictionary<int,T>.Addare explicit interface member implementations.end example
Example: In some cases, the name of an interface member might not be appropriate for the implementing class, in which case, the interface member may be implemented using explicit interface member implementation. A class implementing a file abstraction, for example, would likely implement a
Closemember function that has the effect of releasing the file resource, and implement theDisposemethod of theIDisposableinterface using explicit interface member implementation:interface IDisposable { void Dispose(); } class MyFile : IDisposable { void IDisposable.Dispose() => Close(); public void Close() { // Do what's necessary to close the file System.GC.SuppressFinalize(this); } }end example
It is not possible to access an explicit interface member implementation through its qualified interface member name in a method invocation, property access, event access, or indexer access. An explicit interface instance member implementation can only be accessed through an interface instance, and is in that case referenced simply by its member name. An explicit interface static member implementation can only be accessed through the interface name.
It is a compile-time error for an explicit interface member implementation to include any modifiers (§15.6) other than extern or async.
An explicit interface method implementation inherits any type parameter constraints from the interface.
A type_parameter_constraints_clause on an explicit interface method implementation may only consist of the class or struct primary_constraints applied to type_parameters which are known according to the inherited constraints to be either reference or value types respectively. Any type of the form T? in the signature of the explicit interface method implementation, where T is a type parameter, is interpreted as follows:
- If a
classconstraint is added for type parameterTthenT?is a nullable reference type; otherwise - If either there is no added constraint, or a
structconstraint is added, for the type parameterTthenT?is a nullable value type.
Example: The following demonstrates how the rules work when type parameters are involved:
#nullable enable interface I { void Foo<T>(T? value) where T : class; void Foo<T>(T? value) where T : struct; } class C : I { void I.Foo<T>(T? value) where T : class { } void I.Foo<T>(T? value) where T : struct { } }Without the type parameter constraint
where T : class, the base method with the reference-typed type parameter cannot be overridden. end example
Note: Explicit interface member implementations have different accessibility characteristics than other members. Because explicit interface member implementations are never accessible through a qualified interface member name in a method invocation or a property access, they are in a sense private. However, since they can be accessed through the interface, they are in a sense also as public as the interface in which they are declared. Explicit interface member implementations serve two primary purposes:
- Because explicit interface member implementations are not accessible through class or struct instances, they allow interface implementations to be excluded from the public interface of a class or struct. This is particularly useful when a class or struct implements an internal interface that is of no interest to a consumer of that class or struct.
- Explicit interface member implementations allow disambiguation of interface members with the same signature. Without explicit interface member implementations it would be impossible for a class, struct, or interface to have different implementations of interface members with the same signature and return type, as would it be impossible for a class, struct, or interface to have any implementation at all of interface members with the same signature but with different return types.
end note
For an explicit interface member implementation to be valid, the class, struct, or interface shall name an interface in its base class or base interface list that contains a member whose qualified interface member name, type, number of type parameters, and parameter types exactly match those of the explicit interface member implementation. If an interface function member has a parameter array, the corresponding parameter of an associated explicit interface member implementation is allowed, but not required, to have the params modifier. If the interface function member does not have a parameter array then an associated explicit interface member implementation shall not have a parameter array.
Example: Thus, in the following class
class Shape : ICloneable { object ICloneable.Clone() {...} int IComparable.CompareTo(object other) {...} // invalid }the declaration of
IComparable.CompareToresults in a compile-time error becauseIComparableis not listed in the base class list ofShapeand is not a base interface ofICloneable. Likewise, in the declarationsclass Shape : ICloneable { object ICloneable.Clone() {...} } class Ellipse : Shape { object ICloneable.Clone() {...} // invalid }the declaration of
ICloneable.CloneinEllipseresults in a compile-time error becauseICloneableis not explicitly listed in the base class list ofEllipse.end example
The qualified interface member name of an explicit interface member implementation shall reference the interface in which the member was declared.
Example: Thus, in the declarations
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { void IControl.Paint() {...} void ITextBox.SetText(string text) {...} }the explicit interface member implementation of Paint must be written as
IControl.Paint, notITextBox.Paint.end example
19.6.3 Uniqueness of implemented interfaces
The interfaces implemented by a generic type declaration shall remain unique for all possible constructed types. Without this rule, it would be impossible to determine the correct method to call for certain constructed types.
Example: Suppose a generic class declaration were permitted to be written as follows:
interface I<T> { void F(); } class X<U ,V> : I<U>, I<V> // Error: I<U> and I<V> conflict { void I<U>.F() {...} void I<V>.F() {...} }Were this permitted, it would be impossible to determine which code to execute in the following case:
I<int> x = new X<int, int>(); x.F();end example
To determine if the interface list of a generic type declaration is valid, the following steps are performed:
- Let
Lbe the list of interfaces directly specified in a generic class, struct, or interface declarationC. - Add to
Lany base interfaces of the interfaces already inL. - Remove any duplicates from
L. - If any possible constructed type created from
Cwould, after type arguments are substituted intoL, cause two interfaces inLto be identical, then the declaration ofCis invalid. Constraint declarations are not considered when determining all possible constructed types.
Note: In the class declaration
Xabove, the interface listLconsists ofl<U>andI<V>. The declaration is invalid because any constructed type withUandVbeing the same type would cause these two interfaces to be identical types. end note
It is possible for interfaces specified at different inheritance levels to unify:
interface I<T>
{
void F();
}
class Base<U> : I<U>
{
void I<U>.F() {...}
}
class Derived<U, V> : Base<U>, I<V> // Ok
{
void I<V>.F() {...}
}
This code is valid even though Derived<U,V> implements both I<U> and I<V>. The code
I<int> x = new Derived<int, int>();
x.F();
invokes the method in Derived, since Derived<int,int>' effectively re-implements I<int> (§19.6.7).
19.6.4 Implementation of generic methods
When a generic method implicitly implements an interface method, the constraints given for each method type parameter shall be equivalent in both declarations (after any interface type parameters are replaced with the appropriate type arguments), where method type parameters are identified by ordinal positions, left to right.
Example: In the following code:
interface I<X, Y, Z> { void F<T>(T t) where T : X; void G<T>(T t) where T : Y; void H<T>(T t) where T : Z; } class C : I<object, C, string> { public void F<T>(T t) {...} // Ok public void G<T>(T t) where T : C {...} // Ok public void H<T>(T t) where T : string {...} // Error }the method
C.F<T>implicitly implementsI<object,C,string>.F<T>. In this case,C.F<T>is not required (nor permitted) to specify the constraintT: objectsinceobjectis an implicit constraint on all type parameters. The methodC.G<T>implicitly implementsI<object,C,string>.G<T>because the constraints match those in the interface, after the interface type parameters are replaced with the corresponding type arguments. The constraint for methodC.H<T>is an error because sealed types (stringin this case) cannot be used as constraints. Omitting the constraint would also be an error since constraints of implicit interface method implementations are required to match. Thus, it is impossible to implicitly implementI<object,C,string>.H<T>. This interface method can only be implemented using an explicit interface member implementation:class C : I<object, C, string> { ... public void H<U>(U u) where U : class {...} void I<object, C, string>.H<T>(T t) { string s = t; // Ok H<T>(t); } }In this case, the explicit interface member implementation invokes a public method having strictly weaker constraints. The assignment from t to s is valid since
Tinherits a constraint ofT: string, even though this constraint is not expressible in source code. end example
Note: When a generic method explicitly implements an interface method no constraints are allowed on the implementing method (§15.7.1, §19.6.2). end note
19.6.5 Interface mapping
A class or struct shall provide implementations of all abstract 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, interface, or struct S, starting with C and repeating for each successive base class and implemented interface 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. - Otherwise, if
Scontains a declaration of a non-static public member that matchesM, then this member is the implementation ofI.M. If more than one member matches, it is unspecified which member is the implementation ofI.M. This situation can only occur ifSis a constructed type where the two members as declared in the generic type have different signatures, but the type arguments make their signatures identical.
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 §15.3.3.
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, interface, or struct member A matches an interface member B when:
AandBare methods, and the name, type, and 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 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.Clonemember ofCbecomes the implementation ofCloneinICloneablebecause 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
Paintmethods of bothIControlandIFormare mapped onto thePaintmethod inPage. 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 may need to 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 C1 : IDerived { int IBase.P { get; } int IDerived.P() {...} } class C2 : IDerived { public int P { get; } int IDerived.P() {...} } class C3 : 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
IControlnamed in the base class list, theIControlinherited byITextBox, and theIControlinherited byIListBox. Indeed, there is no notion of a separate identity for these interfaces. Rather, the implementations ofITextBoxandIListBoxshare the same implementation ofIControl, andComboBoxis simply considered to implement three interfaces,IControl,ITextBox, andIListBox.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 { public new void G() {} }the method
FinClass1is used inClass2'simplementation ofInterface1.end example
19.6.6 Interface implementation inheritance
A class inherits all interface implementations provided by its base classes.
Without explicitly re-implementing an interface, a derived class cannot in any way alter the interface mappings it inherits from its base classes.
Example: In the declarations
interface IControl { void Paint(); } class Control : IControl { public void Paint() {...} } class TextBox : Control { public new void Paint() {...} }the
Paintmethod inTextBoxhides thePaintmethod inControl, but it does not alter the mapping ofControl.PaintontoIControl.Paint, and calls toPaintthrough class instances and interface instances will have the following effectsControl c = new Control(); TextBox t = new TextBox(); IControl ic = c; IControl it = t; c.Paint(); // invokes Control.Paint(); t.Paint(); // invokes TextBox.Paint(); ic.Paint(); // invokes Control.Paint(); it.Paint(); // invokes Control.Paint();end example
However, when an interface method is mapped onto a virtual method in a class, it is possible for derived classes to override the virtual method and alter the implementation of the interface.
Example: Rewriting the declarations above to
interface IControl { void Paint(); } class Control : IControl { public virtual void Paint() {...} } class TextBox : Control { public override void Paint() {...} }the following effects will now be observed
Control c = new Control(); TextBox t = new TextBox(); IControl ic = c; IControl it = t; c.Paint(); // invokes Control.Paint(); t.Paint(); // invokes TextBox.Paint(); ic.Paint(); // invokes Control.Paint(); it.Paint(); // invokes TextBox.Paint();end example
Since explicit interface member implementations cannot be declared virtual, it is not possible to override an explicit interface member implementation. However, it is perfectly valid for an explicit interface member implementation to call another method, and that other method can be declared virtual to allow derived classes to override it.
Example:
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() { PaintControl(); } protected virtual void PaintControl() {...} } class TextBox : Control { protected override void PaintControl() {...} }Here, classes derived from
Controlcan specialize the implementation ofIControl.Paintby overriding thePaintControlmethod.end example
19.6.7 Interface re-implementation
A class that inherits an interface implementation is permitted to re-implement the interface by including it in the base class list.
A re-implementation of an interface follows exactly the same interface mapping rules as an initial implementation of an interface. Thus, the inherited interface mapping has no effect whatsoever on the interface mapping established for the re-implementation of the interface.
Example: In the declarations
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() {...} } class MyControl : Control, IControl { public void Paint() {} }the fact that
ControlmapsIControl.PaintontoControl.IControl.Paintdoesn’t affect the re-implementation inMyControl, which mapsIControl.PaintontoMyControl.Paint.end example
Inherited public member declarations and inherited explicit interface member declarations participate in the interface mapping process for re-implemented interfaces.
Example:
interface IMethods { void F(); void G(); void H(); void I(); } class Base : IMethods { void IMethods.F() {} void IMethods.G() {} public void H() {} public void I() {} } class Derived : Base, IMethods { public void F() {} void IMethods.H() {} }Here, the implementation of
IMethodsinDerivedmaps the interface methods ontoDerived.F,Base.IMethods.G,Derived.IMethods.H, andBase.I.end example
When a class implements an interface, it implicitly also implements all that interface’s base interfaces. Likewise, a re-implementation of an interface is also implicitly a re-implementation of all of the interface’s base interfaces.
Example:
interface IBase { void F(); } interface IDerived : IBase { void G(); } class C : IDerived { void IBase.F() {...} void IDerived.G() {...} } class D : C, IDerived { public void F() {...} public void G() {...} }Here, the re-implementation of
IDerivedalso re-implementsIBase, mappingIBase.FontoD.F.end example
19.6.8 Abstract classes and interfaces
Like a non-abstract class, an abstract class shall provide implementations of all abstract members of the interfaces that are listed in the base class list of the class. However, an abstract class is permitted to map interface methods onto abstract methods.
Example:
interface IMethods { void F(); void G(); } abstract class C : IMethods { public abstract void F(); public abstract void G(); }Here, the implementation of
IMethodsmapsFandGonto abstract methods, which shall be overridden in non-abstract classes that derive fromC.end example
Explicit interface member implementations cannot be abstract, but explicit interface member implementations are of course permitted to call abstract methods.
Example:
interface IMethods { void F(); void G(); } abstract class C: IMethods { void IMethods.F() { FF(); } void IMethods.G() { GG(); } protected abstract void FF(); protected abstract void GG(); }Here, non-abstract classes that derive from
Cwould be required to overrideFFandGG, thus providing the actual implementation ofIMethods.end example
ECMA C# draft specification