The members of an interface are the members inherited from the base
interfaces and the members declared by the
interface itself.
interface-member-declarations:
interface-member-declaration
interface-member-declarations interface-member-declaration
interface-member-declaration:
interface-method-declaration
interface-property-declaration
interface-event-declaration
interface-indexer-declaration
An interface declaration may declare zero or more members. The members of
an interface must be methods,
properties, events, or indexers. An interface cannot contain constants,
fields, operators, instance constructors,
destructors, or types, nor can an interface contain static members of any
kind.
All interface members implicitly have public access. It is a compile-time
error for interface member declarations
to include any modifiers. In particular, interface members cannot be
declared with the modifiers abstract,
public, protected, internal, private, virtual, override, or static.
[Example: The example
public delegate void StringListEvent(IStringList sender);
public interface IStringList
{
void Add(string s);
int Count { get; }
event StringListEvent Changed;
Chapter 20 Interfaces
281
string this[int index] { get; set; }
}
declares an interface that contains one each of the possible kinds of
members: A method, a property, an event,
and an indexer. end example]
An interface-declaration creates a new declaration space (§10.3), and the
interface-member-declarations
immediately contained by the interface-declaration introduce new members
into this declaration space. The
following rules apply to interface-member-declarations:
? The name of a method must differ from the names of all properties and
events declared in the same interface.
In addition, the signature (§10.6) of a method must differ from the
signatures of all other methods declared in
the same interface, and two methods declared in the same interface may not
have signatures that differ solely
by ref and out.
? The name of a property or event must differ from the names of all other
members declared in the same
interface.
? The signature of an indexer must differ from the signatures of all other
indexers declared in the same
interface.
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 cause the compiler to issue a warning.
To suppress the warning, the
declaration of the derived interface member must include a new modifier to
indicate that the derived member is
intended to hide the base member. This topic is discussed further in §10.7.1
.2.
If a new modifier is included in a declaration that doesn?t hide an
inherited member, a warning is issued to that
effect. This warning is suppressed by removing the new modifier.
[Note: The members in class object are not, strictly speaking, members of
any interface (§20.2). However, the
members in class object are available via member lookup in any interface
type (§14.3). end note]
20.2.1 Interface methods
Interface methods are declared using interface-method-declarations:
interface-method-declaration:
attributesopt newopt return-type identifier ( formal-parameter-listopt ) ;
The attributes, return-type, identifier, and formal-parameter-list of an
interface method declaration have the same
meaning as those of a method declaration in a class (§17.5). An interface
method declaration is not permitted to
specify a method body, and the declaration therefore always ends with a
semicolon.
20.2.2 Interface properties
Interface properties are declared using interface-property-declarations:
interface-property-declaration:
attributesopt newopt type identifier { interface-accessors }
interface-accessors:
attributesopt get ;
attributesopt set ;
attributesopt get ; attributesopt set ;
attributesopt set ; attributesopt get ;
The attributes, type, and identifier of an interface property declaration
have the same meaning as those of a
property declaration in a class (§17.6).
The accessors of an interface property declaration correspond to the
accessors of a class property declaration
(§17.6.2), except that the accessor body must always be a semicolon. Thus,
the accessors simply indicate whether
the property is read-write, read-only, or write-only.
C# LANGUAGE SPECIFICATION
282
20.2.3 Interface events
Interface events are declared using interface-event-declarations:
interface-event-declaration:
attributesopt newopt event type identifier ;
The attributes, type, and identifier of an interface event declaration have
the same meaning as those of an event
declaration in a class (§17.7).
20.2.4 Interface indexers
Interface indexers are declared using interface-indexer-declarations:
interface-indexer-declaration:
attributesopt newopt type this [ formal-parameter-list ] {
interface-accessors }
The attributes, type, and formal-parameter-list of an interface indexer
declaration have the same meaning as
those of an indexer declaration in a class (§17.8).
The accessors of an interface indexer declaration correspond to the
accessors of a class indexer declaration
(§17.8), except that the accessor body must always be a semicolon. Thus,
the accessors simply indicate whether
the indexer is read-write, read-only, or write-only.
20.2.5 Interface member access
Interface members are accessed through member access (§14.5.4) and indexer
access (§14.5.6.2) expressions of
the form I.M and I[A], where I is an expression having an interface type, M
is a method, property, or event of
that interface type, and A is an indexer argument list.
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 (§14.3), method
invocation (§14.5.5.1), and indexer
access (§14.5.6.2) 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 section
shows several examples of such situations. In all cases, explicit casts can
be used to resolve the ambiguities.
[Example: In the example
interface IList
{
int Count { get; set; }
}
interface ICounter
{
void Count(int i);
}
interface IListCounter: IList, ICounter {}
class C
{
void Test(IListCounter x) {
x.Count(1); // Error
x.Count = 1; // Error
((IList)x).Count = 1; // Ok, invokes IList.Count.set
((ICounter)x).Count(1); // Ok, invokes ICounter.Count
}
}
the first two statements cause compile-time errors because the member
lookup (§14.3) of Count in
IListCounter is ambiguous. As illustrated by the example, the ambiguity is
resolved by casting x to 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 example
Chapter 20 Interfaces
283
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); // Error, both Add methods are applicable
n.Add(1.0); // Ok, only IDouble.Add is applicable
((IInteger)n).Add(1); // Ok, only IInteger.Add is a candidate
((IDouble)n).Add(1); // Ok, only IDouble.Add is a candidate
}
}
the invocation n.Add(1) is ambiguous because a method invocation (§14.5.5.1)
requires all overloaded
candidate methods to be declared in the same type. However, the invocation
n.Add(1.0) is permitted because
only IDouble.Add is applicable. When explicit casts are inserted, there is
only one candidate method, and thus
no ambiguity. end example]
[Example: In the example
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.F member is hidden by the ILeft.F member. The invocation d.F(1)
thus selects ILeft.F, even
though IBase.F appears to not be hidden in the access path that leads
through IRight.
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
IDerived to ILeft to IBase hides
IBase.F, the member is also hidden in the access path from IDerived to
IRight to IBase. end example]
interfaces and the members declared by the
interface itself.
interface-member-declarations:
interface-member-declaration
interface-member-declarations interface-member-declaration
interface-member-declaration:
interface-method-declaration
interface-property-declaration
interface-event-declaration
interface-indexer-declaration
An interface declaration may declare zero or more members. The members of
an interface must be methods,
properties, events, or indexers. An interface cannot contain constants,
fields, operators, instance constructors,
destructors, or types, nor can an interface contain static members of any
kind.
All interface members implicitly have public access. It is a compile-time
error for interface member declarations
to include any modifiers. In particular, interface members cannot be
declared with the modifiers abstract,
public, protected, internal, private, virtual, override, or static.
[Example: The example
public delegate void StringListEvent(IStringList sender);
public interface IStringList
{
void Add(string s);
int Count { get; }
event StringListEvent Changed;
Chapter 20 Interfaces
281
string this[int index] { get; set; }
}
declares an interface that contains one each of the possible kinds of
members: A method, a property, an event,
and an indexer. end example]
An interface-declaration creates a new declaration space (§10.3), and the
interface-member-declarations
immediately contained by the interface-declaration introduce new members
into this declaration space. The
following rules apply to interface-member-declarations:
? The name of a method must differ from the names of all properties and
events declared in the same interface.
In addition, the signature (§10.6) of a method must differ from the
signatures of all other methods declared in
the same interface, and two methods declared in the same interface may not
have signatures that differ solely
by ref and out.
? The name of a property or event must differ from the names of all other
members declared in the same
interface.
? The signature of an indexer must differ from the signatures of all other
indexers declared in the same
interface.
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 cause the compiler to issue a warning.
To suppress the warning, the
declaration of the derived interface member must include a new modifier to
indicate that the derived member is
intended to hide the base member. This topic is discussed further in §10.7.1
.2.
If a new modifier is included in a declaration that doesn?t hide an
inherited member, a warning is issued to that
effect. This warning is suppressed by removing the new modifier.
[Note: The members in class object are not, strictly speaking, members of
any interface (§20.2). However, the
members in class object are available via member lookup in any interface
type (§14.3). end note]
20.2.1 Interface methods
Interface methods are declared using interface-method-declarations:
interface-method-declaration:
attributesopt newopt return-type identifier ( formal-parameter-listopt ) ;
The attributes, return-type, identifier, and formal-parameter-list of an
interface method declaration have the same
meaning as those of a method declaration in a class (§17.5). An interface
method declaration is not permitted to
specify a method body, and the declaration therefore always ends with a
semicolon.
20.2.2 Interface properties
Interface properties are declared using interface-property-declarations:
interface-property-declaration:
attributesopt newopt type identifier { interface-accessors }
interface-accessors:
attributesopt get ;
attributesopt set ;
attributesopt get ; attributesopt set ;
attributesopt set ; attributesopt get ;
The attributes, type, and identifier of an interface property declaration
have the same meaning as those of a
property declaration in a class (§17.6).
The accessors of an interface property declaration correspond to the
accessors of a class property declaration
(§17.6.2), except that the accessor body must always be a semicolon. Thus,
the accessors simply indicate whether
the property is read-write, read-only, or write-only.
C# LANGUAGE SPECIFICATION
282
20.2.3 Interface events
Interface events are declared using interface-event-declarations:
interface-event-declaration:
attributesopt newopt event type identifier ;
The attributes, type, and identifier of an interface event declaration have
the same meaning as those of an event
declaration in a class (§17.7).
20.2.4 Interface indexers
Interface indexers are declared using interface-indexer-declarations:
interface-indexer-declaration:
attributesopt newopt type this [ formal-parameter-list ] {
interface-accessors }
The attributes, type, and formal-parameter-list of an interface indexer
declaration have the same meaning as
those of an indexer declaration in a class (§17.8).
The accessors of an interface indexer declaration correspond to the
accessors of a class indexer declaration
(§17.8), except that the accessor body must always be a semicolon. Thus,
the accessors simply indicate whether
the indexer is read-write, read-only, or write-only.
20.2.5 Interface member access
Interface members are accessed through member access (§14.5.4) and indexer
access (§14.5.6.2) expressions of
the form I.M and I[A], where I is an expression having an interface type, M
is a method, property, or event of
that interface type, and A is an indexer argument list.
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 (§14.3), method
invocation (§14.5.5.1), and indexer
access (§14.5.6.2) 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 section
shows several examples of such situations. In all cases, explicit casts can
be used to resolve the ambiguities.
[Example: In the example
interface IList
{
int Count { get; set; }
}
interface ICounter
{
void Count(int i);
}
interface IListCounter: IList, ICounter {}
class C
{
void Test(IListCounter x) {
x.Count(1); // Error
x.Count = 1; // Error
((IList)x).Count = 1; // Ok, invokes IList.Count.set
((ICounter)x).Count(1); // Ok, invokes ICounter.Count
}
}
the first two statements cause compile-time errors because the member
lookup (§14.3) of Count in
IListCounter is ambiguous. As illustrated by the example, the ambiguity is
resolved by casting x to 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 example
Chapter 20 Interfaces
283
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); // Error, both Add methods are applicable
n.Add(1.0); // Ok, only IDouble.Add is applicable
((IInteger)n).Add(1); // Ok, only IInteger.Add is a candidate
((IDouble)n).Add(1); // Ok, only IDouble.Add is a candidate
}
}
the invocation n.Add(1) is ambiguous because a method invocation (§14.5.5.1)
requires all overloaded
candidate methods to be declared in the same type. However, the invocation
n.Add(1.0) is permitted because
only IDouble.Add is applicable. When explicit casts are inserted, there is
only one candidate method, and thus
no ambiguity. end example]
[Example: In the example
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.F member is hidden by the ILeft.F member. The invocation d.F(1)
thus selects ILeft.F, even
though IBase.F appears to not be hidden in the access path that leads
through IRight.
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
IDerived to ILeft to IBase hides
IBase.F, the member is also hidden in the access path from IDerived to
IRight to IBase. end example]