18 Schnittstellen
18.1 Allgemein
Eine Schnittstelle definiert einen Vertrag. Eine Klasse oder Struktur, die eine Schnittstelle implementiert, muss ihren Vertrag einhalten. Eine Schnittstelle kann von mehreren Basisschnittstellen erben, und eine Klasse oder Struktur kann mehrere Schnittstellen implementieren.
Schnittstellen können Methoden, Eigenschaften, Ereignisse und Indexer enthalten. Die Schnittstelle selbst stellt keine Implementierungen für die Member bereit, die sie deklariert. Die Schnittstelle gibt lediglich die Member an, die von Klassen oder Strukturen bereitgestellt werden sollen, die die Schnittstelle implementieren.
18.2 Schnittstellendeklarationen
18.2.1 Allgemein
Ein interface_declaration ist eine type_declaration (§14.7), die einen neuen Schnittstellentyp deklariert.
interface_declaration
: attributes? interface_modifier* 'partial'? 'interface'
identifier variant_type_parameter_list? interface_base?
type_parameter_constraints_clause* interface_body ';'?
;
Ein interface_declaration besteht aus einem optionalen Satz von Attributen (§22), gefolgt von einem optionalen Satz von interface_modifiers (§18.2.2), gefolgt von einem optionalen Teilmodifizierer (§15.2.7), gefolgt von dem Schlüsselwort interface
und einem Bezeichner , der die Schnittstelle benennt, gefolgt von einer optionalen variant_type_parameter_list Spezifikation (§18.2.3), gefolgt von einem optionalen interface_base Spezifikation (§18.2.4), gefolgt von einer optionalen type_parameter_constraints_clauseSpezifikation (§15.2.5), gefolgt von einem interface_body (§18.3), optional gefolgt von einem Semikolon.
Eine Schnittstellenerklärung stellt nur dann eine type_parameter_constraints_clausezur Verfügung, wenn sie auch eine variant_type_parameter_list bereitstellt.
Eine Schnittstellendeklaration, die eine variant_type_parameter_list bereitstellt, ist eine generische Schnittstellendeklaration. Darüber hinaus ist jede Schnittstelle, die in einer generischen Klassendeklaration oder einer generischen Strukturdeklaration geschachtelt ist, selbst eine generische Schnittstellendeklaration, da Typargumente für den enthaltenden Typ bereitgestellt werden müssen, um einen konstruierten Typ (§8.4) zu erstellen.
18.2.2 Schnittstellenmodifizierer
Ein interface_declaration kann optional eine Sequenz von Schnittstellenmodifizierern enthalten:
interface_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§23.2) ist nur im unsicheren Code (§23) verfügbar.
Es handelt sich um einen Kompilierungsfehler für denselben Modifizierer, der mehrmals in einer Schnittstellendeklaration angezeigt wird.
Der new
Modifizierer ist nur für Schnittstellen zulässig, die innerhalb einer Klasse definiert sind. Es gibt an, dass die Schnittstelle ein geerbtes Element mit demselben Namen ausblendet, wie in §15.3.5 beschrieben.
Die public
, protected
, internal
und private
Modifizierer steuern die Barrierefreiheit der Schnittstelle. Je nach Kontext, in dem die Schnittstellendeklaration auftritt, sind möglicherweise nur einige dieser Modifizierer zulässig (§7.5.2). Wenn eine Teiltypdeklaration (§15.2.7) eine Barrierefreiheitsspezifikation enthält (über die public
, protected
, , internal
und private
Modifizierer), gelten die Regeln in §15.2.2 .
18.2.3 Variant-Typparameterlisten
18.2.3.1 Allgemein
Variantentypparameterlisten können nur für Schnittstellen- und Delegattypen auftreten. Der Unterschied zu gewöhnlichen type_parameter_lists ist die optionale variance_annotation für jeden Typparameter.
variant_type_parameter_list
: '<' variant_type_parameters '>'
;
variant_type_parameters
: attributes? variance_annotation? type_parameter
| variant_type_parameters ',' attributes? variance_annotation?
type_parameter
;
variance_annotation
: 'in'
| 'out'
;
Wenn die Varianzanmerkung lautetout
, wird der Typparameter als kovariant bezeichnet. Wenn die Varianzanmerkung lautetin
, wird der Typparameter als kontravariant bezeichnet. Wenn keine Varianzanmerkung vorhanden ist, wird der Typparameter als invariant bezeichnet.
Beispiel: Im Folgenden:
interface C<out X, in Y, Z> { X M(Y y); Z P { get; set; } }
X
ist kovariant,Y
ist kontravariant undZ
ist invariant.Endbeispiel
Wenn eine generische Schnittstelle in mehreren Teilen deklariert wird (§15.2.3), muss jede Teildeklaration die gleiche Varianz für jeden Typparameter angeben.
18.2.3.2 Varianzsicherheit
Das Vorkommen von Varianzanmerkungen in der Typparameterliste eines Typs schränkt die Orte ein, an denen Typen innerhalb der Typdeklaration auftreten können.
Ein Typ T ist ausgabeunsicher , wenn einer der folgenden Haltebereiche enthält:
T
ist ein kontravarianter Typparameter.T
ist ein Arraytyp mit einem ausgabeunsicheren ElementtypT
ist eine Schnittstelle oder ein DelegattypSᵢ,... Aₑ
, der aus einem generischen TypS<Xᵢ, ... Xₑ>
erstellt wird, wobei mindestens einerAᵢ
der folgenden Haltebereiche gilt:Xᵢ
ist kovariant oder invariant undAᵢ
ist ausgabeunsicher.Xᵢ
ist kontravariant oder invariant undAᵢ
ist eingabeunsicher.
Ein Typ T ist eingabeunsicher , wenn einer der folgenden Haltebereiche enthält:
T
ist ein kovarianter TypparameterT
ist ein Arraytyp mit einem eingabeunsicheren ElementtypT
ist eine Schnittstelle oder ein DelegattypS<Aᵢ,... Aₑ>
, der aus einem generischen TypS<Xᵢ, ... Xₑ>
erstellt wird, wobei mindestens einerAᵢ
der folgenden Haltebereiche gilt:Xᵢ
ist kovariant oder invariant undAᵢ
ist eingabeunsicher.Xᵢ
ist kontravariant oder invariant undAᵢ
ist ausgabeunsicher.
Intuitiv ist ein ausgabeunsicherer Typ an einer Ausgabeposition verboten, und ein eingabeunsicherer Typ ist an einer Eingabeposition verboten.
Ein Typ ist ausgabesicher, wenn er nicht ausgabeunsicher ist und eingabesicher ist, wenn er nicht eingabeunsicher ist.
18.2.3.3 Varianzumwandlung
Der Zweck von Varianzanmerkungen besteht darin, flexiblere (aber dennoch typsichere) Konvertierungen in Schnittstellen- und Delegattypen bereitzustellen. Zu diesem Zweck verwenden die Definitionen impliziter (§10.2) und expliziter Konvertierungen (§10.3) den Begriff der Varianzkonvertierbarkeit, der wie folgt definiert ist:
Ein Typ T<Aᵢ, ..., Aᵥ>
ist varianzkonvertierbar in einen Typ T<Bᵢ, ..., Bᵥ>
, wenn T
es sich um eine Schnittstelle oder einen Delegattyp handelt, der mit den Variantentypparametern T<Xᵢ, ..., Xᵥ>
deklariert ist, und für jeden Variant-Typparameter Xᵢ
einer der folgenden Haltebereiche:
Xᵢ
ist kovariant und eine implizite Referenz- oder Identitätskonvertierung ist vonAᵢ
zuBᵢ
Xᵢ
ist kontravariant und eine implizite Referenz- oder Identitätskonvertierung ist vonBᵢ
zuAᵢ
Xᵢ
ist invariant und eine Identitätskonvertierung ist vonAᵢ
zuBᵢ
18.2.4 Basisschnittstellen
Eine Schnittstelle kann von null oder mehr Schnittstellentypen erben, die als explizite Basisschnittstellen der Schnittstelle bezeichnet werden. Wenn eine Schnittstelle über eine oder mehrere explizite Basisschnittstellen verfügt, folgt in der Deklaration dieser Schnittstelle der Schnittstellenbezeichner ein Doppelpunkt und eine durch Trennzeichen getrennte Liste der Basisschnittstellentypen.
interface_base
: ':' interface_type_list
;
Die expliziten Basisschnittstellen können Schnittstellentypen (§8.4, §18.2) konstruiert werden. Eine Basisschnittstelle kann nicht eigenständig ein Typparameter sein, obwohl sie die Typparameter einbeziehen kann, die sich im Bereich befinden.
Für einen konstruierten Schnittstellentyp werden die expliziten Basisschnittstellen gebildet, indem die expliziten Basisschnittstellendeklarationen für die generische Typdeklaration verwendet werden, und für jede type_parameter in der Basisschnittstellendeklaration die entsprechenden type_argument des konstruierten Typs ersetzt werden.
Die expliziten Basisschnittstellen einer Schnittstelle müssen mindestens so zugänglich sein wie die Schnittstelle selbst (§7.5.5).
Hinweis: Beispielsweise handelt es sich um einen Kompilierungszeitfehler, um eine
private
Schnittstelle iminternal
interface_base einerpublic
Schnittstelle anzugeben. Endnote
Es handelt sich um einen Kompilierungszeitfehler für eine Schnittstelle, die direkt oder indirekt von sich selbst erbt.
Die Basisschnittstellen einer Schnittstelle sind die expliziten Basisschnittstellen und ihre Basisschnittstellen. Mit anderen Worten, der Satz von Basisschnittstellen ist die vollständige transitive Schließung der expliziten Basisschnittstellen, ihre expliziten Basisschnittstellen usw. Eine Schnittstelle erbt alle Member ihrer Basisschnittstellen.
Beispiel: Im folgenden Code
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } interface IListBox : IControl { void SetItems(string[] items); } interface IComboBox: ITextBox, IListBox {}
die Basisschnittstellen von
IComboBox
sindIControl
,ITextBox
undIListBox
. Mit anderen Worten, dieIComboBox
schnittstelle oben erbt MemberSetText
undSetItems
sowiePaint
.Endbeispiel
Elemente, die von einem konstruierten generischen Typ geerbt werden, werden nach der Typersetzung geerbt. Das heißt, alle Bestandteiltypen im Element haben die Typparameter der Basisklassendeklaration durch die entsprechenden Typargumente ersetzt, die in der class_base Spezifikation verwendet werden.
Beispiel: Im folgenden Code
interface IBase<T> { T[] Combine(T a, T b); } interface IDerived : IBase<string[,]> { // Inherited: string[][,] Combine(string[,] a, string[,] b); }
die Schnittstelle
IDerived
erbt dieCombine
Methode, nachdem der TypparameterT
durchstring[,]
ersetzt wurde.Endbeispiel
Eine Klasse oder Struktur, die eine Schnittstelle implementiert, implementiert auch implizit alle Basisschnittstellen der Schnittstelle.
Die Handhabung von Schnittstellen zu mehreren Teilen einer Teilschnittstellendeklaration (§15.2.7) wird in §15.2.4.3 weiter erläutert.
Jede Basisschnittstelle einer Schnittstelle muss ausgabesicher sein (§18.2.3.2).
18.3 Schnittstellentext
Die interface_body einer Schnittstelle definiert die Member der Schnittstelle.
interface_body
: '{' interface_member_declaration* '}'
;
18.4 Schnittstellenmitglieder
18.4.1 Allgemein
Die Member einer Schnittstelle sind die Elemente, die von den Basisschnittstellen geerbt werden, und die Elemente, die von der Schnittstelle selbst deklariert wurden.
interface_member_declaration
: interface_method_declaration
| interface_property_declaration
| interface_event_declaration
| interface_indexer_declaration
;
Eine Schnittstellendeklaration deklariert null oder mehr Member. Die Member einer Schnittstelle müssen Methoden, Eigenschaften, Ereignisse oder Indexer sein. Eine Schnittstelle kann weder Konstanten, Felder, Operatoren, Instanzkonstruktoren, Finalizer oder Typen enthalten noch eine Schnittstelle statische Elemente beliebiger Art enthalten.
Alle Schnittstellenmitglieder haben implizit öffentlichen Zugriff. Es handelt sich um einen Kompilierungszeitfehler für Schnittstellenmembezeichnerdeklarationen, die alle Modifizierer enthalten.
Ein interface_declaration erstellt einen neuen Deklarationsraum (§7.3) und die Typparameter und interface_member_declarationunmittelbar in der interface_declaration neue Member in diesen Deklarationsbereich einführen. Die folgenden Regeln gelten für interface_member_declaration:
- Der Name eines Typparameters in der variant_type_parameter_list einer Schnittstellendeklaration unterscheidet sich von den Namen aller anderen Typparameter in demselben variant_type_parameter_list und muss sich von den Namen aller Member der Schnittstelle unterscheiden.
- Der Name einer Methode muss sich von den Namen aller Eigenschaften und Ereignisse unterscheiden, die in derselben Schnittstelle deklariert sind. Darüber hinaus unterscheidet sich die Signatur (§7.6) einer Methode von den Signaturen aller anderen Methoden, die in derselben Schnittstelle deklariert sind, und zwei methoden, die in derselben Schnittstelle deklariert sind, dürfen keine Signaturen aufweisen, die sich ausschließlich von
in
,out
, undref
. - Der Name einer Eigenschaft oder eines Ereignisses unterscheidet sich von den Namen aller anderen Mitglieder, die in derselben Schnittstelle deklariert sind.
- Die Signatur eines Indexers unterscheidet sich von den Signaturen aller anderen in derselben Schnittstelle deklarierten Indexer.
Die geerbten Member einer Schnittstelle sind ausdrücklich nicht Teil des Deklarationsraums der Schnittstelle. Daher kann eine Schnittstelle ein Element mit demselben Namen oder derselben Signatur wie ein geerbtes Element deklarieren. Wenn dies geschieht, wird das abgeleitete Schnittstellenmemmemm gesagt , das Basisschnittstellenelement auszublenden . Das Ausblenden eines geerbten Elements wird nicht als Fehler betrachtet, führt jedoch dazu, dass der Compiler eine Warnung ausgibt. Um die Warnung zu unterdrücken, muss die Deklaration des abgeleiteten Schnittstellenelements einen new
Modifizierer enthalten, um anzugeben, dass das abgeleitete Element das Basiselement ausblenden soll. Dieses Thema wird weiter in §7.7.2.3 erörtert.
Wenn ein new
Modifizierer in einer Deklaration enthalten ist, die kein geerbtes Element ausblendet, wird zu diesem Effekt eine Warnung ausgegeben. Diese Warnung wird durch Entfernen des new
Modifizierers unterdrückt.
Hinweis: Die Mitglieder in der Klasse
object
sind nicht streng genommen Member einer Schnittstelle (§18.4). Die Member in der Klasseobject
sind jedoch über die Membersuche in einem beliebigen Schnittstellentyp (§12.5) verfügbar. Endnote
Die Gruppe von Mitgliedern einer Schnittstelle, die in mehreren Teilen deklariert ist (§15.2.7) ist die Vereinigung der mitglieder, die in jedem Teil deklariert sind. Die Körper aller Teile der Schnittstellendeklaration haben denselben Deklarationsbereich (§7.3), und der Umfang jedes Mitglieds (§7.7) erstreckt sich auf die Körper aller Teile.
18.4.2 Schnittstellenmethoden
Schnittstellenmethoden werden mit interface_method_declaration sdeklariert:
interface_method_declaration
: attributes? 'new'? return_type interface_method_header
| attributes? 'new'? ref_kind ref_return_type interface_method_header
;
interface_method_header
: identifier '(' parameter_list? ')' ';'
| identifier type_parameter_list '(' parameter_list? ')'
type_parameter_constraints_clause* ';'
;
Die Attribute, return_type, ref_return_type, Bezeichner und parameter_list einer Schnittstellenmethodendeklaration haben die gleiche Bedeutung wie die attribute einer Methodendeklaration in einer Klasse (§15.6). Eine Schnittstellenmethodendeklaration darf keinen Methodentext angeben, und die Deklaration endet daher immer mit einem Semikolon.
Alle Parametertypen einer Schnittstellenmethode müssen eingabesicher sein (§18.2.3.2), und der Rückgabetyp muss entweder void
oder ausgabesicher sein. Darüber hinaus müssen alle Ausgabe- oder Referenzparametertypen auch ausgabesicher sein.
Hinweis: Ausgabeparameter müssen aufgrund allgemeiner Implementierungseinschränkungen eingabesicher sein. Endnote
Darüber hinaus müssen jede Klassentypeinschränkung, Schnittstellentypeinschränkung und Typparametereinschränkung für alle Typparameter der Methode eingabesicher sein.
Darüber hinaus müssen jede Klassentypeinschränkung, Schnittstellentypeinschränkung und Typparametereinschränkung für jeden Typparameter der Methode eingabesicher sein.
Diese Regeln stellen sicher, dass jede kovariante oder kontravariante Verwendung der Schnittstelle typsicher bleibt.
Beispiel:
interface I<out T> { void M<U>() where U : T; // Error }
ist unformiert, da die Verwendung
T
als TypparametereinschränkungU
nicht eingabesicher ist.Wäre diese Einschränkung nicht vorhanden, wäre es möglich, die Typsicherheit auf folgende Weise zu verletzen:
class B {} class D : B {} class E : B {} class C : I<D> { public void M<U>() {...} } ... I<B> b = new C(); b.M<E>();
Dies ist eigentlich ein Aufruf von
C.M<E>
. Aber dieser Aufruf erfordert, dassE
diese ableitenD
, so dass die Typsicherheit hier verletzt würde.Endbeispiel
18.4.3 Schnittstelleneigenschaften
Schnittstelleneigenschaften werden mit interface_property_declarations deklariert:
interface_property_declaration
: attributes? 'new'? type identifier '{' interface_accessors '}'
| attributes? 'new'? ref_kind type identifier '{' ref_interface_accessor '}'
;
interface_accessors
: attributes? 'get' ';'
| attributes? 'set' ';'
| attributes? 'get' ';' attributes? 'set' ';'
| attributes? 'set' ';' attributes? 'get' ';'
;
ref_interface_accessor
: attributes? 'get' ';'
;
Die Attribute, Typ und Bezeichner einer Schnittstelleneigenschaftsdeklaration haben die gleiche Bedeutung wie die attribute einer Eigenschaftsdeklaration in einer Klasse (§15.7).
Die Accessoren einer Schnittstelleneigenschaftsdeklaration entsprechen den Accessoren einer Klasseneigenschaftsdeklaration (§15.7.3), mit der Ausnahme, dass die accessor_body immer ein Semikolon sein soll. Daher geben die Accessoren einfach an, ob die Eigenschaft schreibgeschützt, schreibgeschützt oder schreibgeschützt ist.
Der Typ einer Schnittstelleneigenschaft muss ausgabesicher sein, wenn ein Get-Accessor vorhanden ist und eingabesicher sein muss, wenn ein festgelegter Accessor vorhanden ist.
18.4.4 Schnittstellenereignisse
Schnittstellenereignisse werden mit interface_event_declarations deklariert:
interface_event_declaration
: attributes? 'new'? 'event' type identifier ';'
;
Die Attribute, Typ und Bezeichner einer Schnittstellenereignisdeklaration haben die gleiche Bedeutung wie die attribute einer Ereignisdeklaration in einer Klasse (§15.8).
Der Typ eines Schnittstellenereignisses muss eingabesicher sein.
18.4.5 Schnittstellenindexer
Schnittstellenindexer werden mit interface_indexer_declarations deklariert:
interface_indexer_declaration
: attributes? 'new'? type 'this' '[' parameter_list ']'
'{' interface_accessors '}'
| attributes? 'new'? ref_kind type 'this' '[' parameter_list ']'
'{' ref_interface_accessor '}'
;
Die Attribute, Typ und parameter_list einer Schnittstellenindexerdeklaration haben die gleiche Bedeutung wie die attribute einer Indexerdeklaration in einer Klasse (§15.9).
Die Accessoren einer Schnittstellenindexerdeklaration entsprechen den Accessoren einer Klassenindexdeklaration (§15.9), mit der Ausnahme, dass die accessor_body immer ein Semikolon sein soll. Daher geben die Accessoren einfach an, ob der Indexer schreibgeschützt, schreibgeschützt oder schreibgeschützt ist.
Alle Parametertypen eines Schnittstellenindexers müssen eingabesicher sein (§18.2.3.2). Darüber hinaus müssen alle Ausgabe- oder Referenzparametertypen auch ausgabesicher sein.
Hinweis: Ausgabeparameter müssen aufgrund allgemeiner Implementierungseinschränkungen eingabesicher sein. Endnote
Der Typ eines Schnittstellenindexers muss ausgabesicher sein, wenn ein Get-Accessor vorhanden ist und eingabesicher sein muss, wenn ein festgelegter Accessor vorhanden ist.
18.4.6 Schnittstellenmitgliedszugriff
Auf Schnittstellenmember wird über Memberzugriffsausdrücke (§12.8.7) und Indizierungszugriff (§12.8.11.3) des Formulars I.M
zugegriffen, wobei I[A]
es sich um I
einen Schnittstellentyp handelt, eine Methode, M
Eigenschaft oder ein Ereignis dieses Schnittstellentyps ist und A
eine Indexerargumentliste ist.
Bei Schnittstellen, die streng single-vererbung sind (jede Schnittstelle in der Vererbungskette hat genau null oder eine direkte Basisschnittstelle), sind die Auswirkungen der Membersuche (§12.5), Methodenaufrufe (§12.8.9.2) und Indexerzugriff (§12.8.11.3) exakt mit Klassen und Strukturen identisch: Weniger abgeleitete Member verbergen weniger abgeleitete Member mit demselben Namen oder derselben Signatur. Bei Schnittstellen mit mehreren Vererbungen können jedoch Mehrdeutigkeiten auftreten, wenn zwei oder mehr nicht verwandte Basisschnittstellen Member mit demselben Namen oder derselben Signatur deklarieren. Diese Unterliste zeigt mehrere Beispiele, von denen einige zu Mehrdeutigkeiten und anderen führen, die nicht. In allen Fällen können explizite Umwandlungen verwendet werden, um die Mehrdeutigkeiten aufzulösen.
Beispiel: Im folgenden Code
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 } }
die ersten beiden Anweisungen führen zu Kompilierungsfehlern, da die Elementsuche (§12.5) von
Count
inIListCounter
mehrdeutig ist. Wie im Beispiel dargestellt, wird die Mehrdeutigkeit durch Umwandlungx
in den entsprechenden Basisschnittstellentyp aufgelöst. Solche Umwandlungen haben keine Laufzeitkosten – sie bestehen lediglich darin, die Instanz zur Kompilierungszeit als weniger abgeleiteten Typ anzuzeigen.Endbeispiel
Beispiel: Im folgenden 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 } }
der Aufruf
n.Add(1)
wähltIInteger.Add
aus, indem Überlastungsregeln von §12.6.4 angewendet werden. Ebenso wähltIDouble.Add
der Aufrufn.Add(1.0)
aus. Wenn explizite Umwandlungen eingefügt werden, gibt es nur eine Kandidatenmethode und somit keine Mehrdeutigkeit.Endbeispiel
Beispiel: Im folgenden 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 } }
das
IBase.F
Mitglied vom Element ausgeblendetILeft.F
ist. Der Aufrufd.F(1)
wähltILeft.F
daher aus, auch wennIBase.F
er nicht in dem Durchgangspfad verborgen erscheint, der durchIRight
führt.Die intuitive Regel zum Ausblenden in Schnittstellen mit mehreren Vererbungen ist einfach dies: Wenn ein Element in einem beliebigen Zugriffspfad ausgeblendet ist, wird es in allen Zugriffspfaden ausgeblendet. Da der Zugriffspfad von
IDerived
zuILeft
zuIBase
ausblendenIBase.F
ist, wird das Element auch im Zugriffspfad vonIDerived
zu " zuIRight
"IBase
ausgeblendet.Endbeispiel
18.5 Qualifizierte Schnittstellenmitgliedsnamen
Ein Schnittstellenelement wird manchmal durch den Namen des qualifizierten Schnittstellenmitglieds bezeichnet. Der qualifizierte Name eines Schnittstellenelements besteht aus dem Namen der Schnittstelle, in der das Element deklariert wird, gefolgt von einem Punkt, gefolgt vom Namen des Elements. Der qualifizierte Name eines Mitglieds verweist auf die Schnittstelle, in der das Element deklariert wird.
Beispiel: Angesichts der Deklarationen
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); }
der qualifizierte Name ist
Paint
IControl.Paint
und der qualifizierte Name von SetText lautetITextBox.SetText
. Im obigen Beispiel ist es nicht möglich, aufPaint
ITextBox.Paint
.Endbeispiel
Wenn eine Schnittstelle Teil eines Namespaces ist, kann ein qualifizierter Schnittstellenmemmname den Namespacenamen enthalten.
Beispiel:
namespace System { public interface ICloneable { object Clone(); } }
Innerhalb des
System
Namespaces sind sowohl als auchICloneable.Clone
System.ICloneable.Clone
qualifizierte Schnittstellenmememmnamen für dieClone
Methode vorhanden.Endbeispiel
18.6 Schnittstellenimplementierungen
18.6.1 Allgemein
Schnittstellen können von Klassen und Strukturen implementiert werden. Um anzugeben, dass eine Klasse oder Struktur direkt eine Schnittstelle implementiert, ist die Schnittstelle in der Basisklassenliste der Klasse oder Struktur enthalten.
Beispiel:
interface ICloneable { object Clone(); } interface IComparable { int CompareTo(object other); } class ListEntry : ICloneable, IComparable { public object Clone() {...} public int CompareTo(object other) {...} }
Endbeispiel
Eine Klasse oder Struktur, die direkt eine Schnittstelle implementiert, implementiert auch implizit alle Basisschnittstellen der Schnittstelle. Dies gilt auch, wenn die Klasse oder Struktur nicht explizit alle Basisschnittstellen in der Basisklassenliste auflistet.
Beispiel:
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { public void Paint() {...} public void SetText(string text) {...} }
Hier implementiert die Klasse
TextBox
sowohl alsITextBox
auchIControl
.Endbeispiel
Wenn eine Klasse C
direkt eine Schnittstelle implementiert, implementieren alle von C
der Schnittstelle abgeleiteten Klassen implizit die Schnittstelle.
Die in einer Klassendeklaration angegebenen Basisschnittstellen können Schnittstellentypen (§8.4, §18.2) konstruiert werden.
Beispiel: Der folgende Code veranschaulicht, wie eine Klasse konstruierte Schnittstellentypen implementieren kann:
class C<U, V> {} interface I1<V> {} class D : C<string, int>, I1<string> {} class E<T> : C<int, T>, I1<T> {}
Endbeispiel
Die Basisschnittstellen einer generischen Klassendeklaration erfüllen die in §18.6.3 beschriebene Eindeutigkeitsregel.
18.6.2 Explizite Schnittstellenmemimplementierungen
Für die Implementierung von Schnittstellen kann eine Klasse oder Struktur explizite Schnittstellenmememimplementierungen deklarieren. Eine explizite Schnittstellenmememingimplementierung ist eine Methoden-, Eigenschafts-, Ereignis- oder Indexerdeklaration, die auf einen qualifizierten Schnittstellenmemembenamen verweist.
Beispiel:
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) {...} }
Hier
IDictionary<int,T>.this
undIDictionary<int,T>.Add
sind explizite Schnittstellenmememimplementierungen.Endbeispiel
Beispiel: In einigen Fällen ist der Name eines Schnittstellenelements möglicherweise nicht für die implementierungsklasse geeignet, in diesem Fall kann das Schnittstellenmememm mithilfe einer expliziten Schnittstellenmememerimplementierung implementiert werden. Eine Klasse, die eine Dateistraktion implementiert, würde z. B. wahrscheinlich eine
Close
Memberfunktion implementieren, die auswirkungen auf die Freigabe der Dateiressource hat, und dieDispose
Methode derIDisposable
Schnittstelle mithilfe einer expliziten Schnittstellenmemmimplementierung implementieren: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); } }
Endbeispiel
Es ist nicht möglich, über den qualifizierten Schnittstellenmememmnamen in einem Methodenaufruf, Eigenschaftenzugriff, Ereigniszugriff oder Indexerzugriff auf eine explizite Schnittstellenmememingimplementierung zuzugreifen. Auf eine explizite Schnittstellenmememoverimplementierung kann nur über eine Schnittstelleninstanz zugegriffen werden. In diesem Fall wird einfach durch den Membernamen verwiesen.
Es handelt sich um einen Kompilierungsfehler für eine explizite Schnittstellenmemmimplementierung, um andere Modifizierer (§15.6) als extern
oder async
.
Es handelt sich um einen Kompilierungsfehler für eine explizite Schnittstellenmethodenimplementierung, um type_parameter_constraints_clauses einzuschließen. Die Einschränkungen für eine generische explizite Schnittstellenmethodenimplementierung werden von der Schnittstellenmethode geerbt.
Hinweis: Explizite Schnittstellenmemberimplementierungen weisen unterschiedliche Barrierefreiheitsmerkmale auf als andere Member. Da auf explizite Schnittstellenmememimplementierungen niemals über einen qualifizierten Schnittstellenmemembenamen in einem Methodenaufruf oder einem Eigenschaftszugriff zugegriffen werden kann, sind sie in einem Sinne privat. Da sie jedoch über die Schnittstelle aufgerufen werden können, sind sie auch so öffentlich wie die Schnittstelle, in der sie deklariert werden. Explizite Schnittstellenmememimplementierungen dienen zwei hauptzwecke:
- Da auf explizite Schnittstellenmememimplementierungen nicht über Klassen- oder Strukturinstanzen zugegriffen werden kann, können Schnittstellenimplementierungen von der öffentlichen Schnittstelle einer Klasse oder Struktur ausgeschlossen werden. Dies ist besonders nützlich, wenn eine Klasse oder Struktur eine interne Schnittstelle implementiert, die für einen Verbraucher dieser Klasse oder Struktur nicht von Interesse ist.
- Explizite Schnittstellenmemberimplementierungen ermöglichen eine Mehrdeutigkeit der Schnittstellenmember mit derselben Signatur. Ohne explizite Schnittstellenmemberimplementierungen wäre es für eine Klasse oder Struktur unmöglich, unterschiedliche Implementierungen von Schnittstellenmembern mit derselben Signatur und demselben Rückgabetyp zu haben, da es unmöglich wäre, dass eine Klasse oder Struktur überhaupt eine Implementierung über alle Schnittstellenmember mit derselben Signatur, aber mit unterschiedlichen Rückgabetypen verfügt.
Endnote
Damit eine explizite Schnittstellenmemimplementierung gültig ist, muss die Klasse oder Struktur eine Schnittstelle in der Basisklassenliste benennen, die ein Mitglied enthält, dessen qualifizierter Schnittstellenmememmname, Typ, Anzahl von Typparametern und Parametertypen genau mit denen der expliziten Schnittstellenmemimplementierung übereinstimmen. Wenn ein Schnittstellenfunktionselement über ein Parameterarray verfügt, ist der entsprechende Parameter einer zugeordneten expliziten Schnittstellenelementimplementierung zulässig, aber nicht erforderlich, um den params
Modifizierer zu haben. Wenn der Schnittstellenfunktionsmemm kein Parameterarray besitzt, darf eine zugeordnete explizite Schnittstellenmememimplementierung kein Parameterarray aufweisen.
Beispiel: Daher in der folgenden Klasse
class Shape : ICloneable { object ICloneable.Clone() {...} int IComparable.CompareTo(object other) {...} // invalid }
die Deklaration der
IComparable.CompareTo
Ergebnisse in einem Kompilierungszeitfehler, daIComparable
sie nicht in der BasisklassenlisteShape
aufgeführt ist und keine Basisschnittstelle vonICloneable
. Ebenso in den Deklarationenclass Shape : ICloneable { object ICloneable.Clone() {...} } class Ellipse : Shape { object ICloneable.Clone() {...} // invalid }
die Deklaration von
ICloneable.Clone
inEllipse
results in a compile-time error becauseICloneable
is not explicitly listed in the base class list ofEllipse
.Endbeispiel
Der Name des qualifizierten Schnittstellenmitglieds einer expliziten Schnittstellenmitgliedsimplementierung verweist auf die Schnittstelle, in der das Mitglied deklariert wurde.
Beispiel: In den Deklarationen
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { void IControl.Paint() {...} void ITextBox.SetText(string text) {...} }
die explizite Schnittstellenelementimplementierung von Paint muss geschrieben werden,
IControl.Paint
nichtITextBox.Paint
.Endbeispiel
18.6.3 Eindeutigkeit implementierter Schnittstellen
Die von einer generischen Typdeklaration implementierten Schnittstellen müssen für alle möglichen konstruierten Typen eindeutig bleiben. Ohne diese Regel wäre es unmöglich, die richtige Methode für bestimmte konstruierte Typen aufzurufen.
Beispiel: Angenommen, eine generische Klassendeklaration darf wie folgt geschrieben werden:
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() {...} }
Wenn dies zulässig wäre, wäre es unmöglich zu bestimmen, welcher Code im folgenden Fall ausgeführt werden soll:
I<int> x = new X<int, int>(); x.F();
Endbeispiel
Um festzustellen, ob die Schnittstellenliste einer generischen Typdeklaration gültig ist, werden die folgenden Schritte ausgeführt:
- Lassen Sie uns
L
die Liste der Schnittstellen sein, die direkt in einer generischen Klasse, Struktur oder SchnittstellendeklarationC
angegeben sind. L
Fügen Sie alle Basisschnittstellen der Schnittstellen hinzu, die bereits vorhandenL
sind.- Entfernen Sie alle Duplikate aus
L
. - Wenn ein möglicher konstruierter Typ, der erstellt
C
wurde, nach dem Ersetzen von Typargumenten inL
, bewirkt, dass zwei SchnittstellenL
identisch sind, dann ist die DeklarationC
ungültig. Einschränkungsdeklarationen werden beim Ermitteln aller möglichen konstruierten Typen nicht berücksichtigt.
Hinweis: In der obigen Klassendeklaration
X
besteht die SchnittstellenlisteL
ausl<U>
undI<V>
. Die Deklaration ist ungültig, da jeder konstruierte Typ mitU
undV
derselbe Typ dazu führen würde, dass diese beiden Schnittstellen identische Typen sind. Endnote
Es ist möglich, schnittstellen, die auf unterschiedlichen Vererbungsebenen angegeben sind, zu vereinheitlichen:
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() {...}
}
Dieser Code ist gültig, auch wenn Derived<U,V>
er sowohl implementiert I<V>
als auch I<U>
. Der Code
I<int> x = new Derived<int, int>();
x.F();
ruft die Methode in Derived
auf, da Derived<int,int>'
sie effektiv neu implementiert I<int>
wird (§18.6.7).
18.6.4 Implementierung generischer Methoden
Wenn eine generische Methode implizit eine Schnittstellenmethode implementiert, müssen die für jeden Methodentypparameter angegebenen Einschränkungen in beiden Deklarationen gleich sein (nachdem alle Schnittstellentypparameter durch die entsprechenden Typargumente ersetzt wurden), wobei Methodentypparameter durch Ordnungspositionen von links nach rechts identifiziert werden.
Beispiel: Im folgenden 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 }
die Methode
C.F<T>
implizit implementiertI<object,C,string>.F<T>
. In diesem Fall ist es nicht erforderlich (oder zulässig), die EinschränkungT: object
anzugeben,C.F<T>
daobject
es sich um eine implizite Einschränkung für alle Typparameter handelt. Die MethodeC.G<T>
implementiertI<object,C,string>.G<T>
implizit, da die Einschränkungen denen in der Schnittstelle entsprechen, nachdem die Schnittstellentypparameter durch die entsprechenden Typargumente ersetzt wurden. Die Einschränkung für die MethodeC.H<T>
ist ein Fehler, da versiegelte Typen (string
in diesem Fall) nicht als Einschränkungen verwendet werden können. Das Weglassen der Einschränkung wäre auch ein Fehler, da Einschränkungen impliziter Schnittstellenmethodenimplementierungen übereinstimmen müssen. Daher ist es unmöglich, implizit zu implementierenI<object,C,string>.H<T>
. Diese Schnittstellenmethode kann nur mithilfe einer expliziten Schnittstellenelementimplementierung implementiert werden: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 diesem Fall ruft die explizite Schnittstellenmitgliedsimplementierung eine öffentliche Methode mit streng schwächeren Einschränkungen auf. Die Zuordnung von t zu s ist gültig, da
T
sie eine EinschränkungT: string
erbt, auch wenn diese Einschränkung im Quellcode nicht ausgedrückt werden kann. Endbeispiel
Hinweis: Wenn eine generische Methode explizit eine Schnittstellenmethode implementiert, sind für die Implementierungsmethode keine Einschränkungen zulässig (§15.7.1, §18.6.2). Endnote
18.6.5 Schnittstellenzuordnung
Eine Klasse oder Struktur stellt Implementierungen aller Member der Schnittstellen bereit, die in der Basisklassenliste der Klasse oder Struktur aufgeführt sind. Der Prozess zum Auffinden von Implementierungen von Schnittstellenmitgliedern in einer implementierenden Klasse oder Struktur wird als Schnittstellenzuordnung bezeichnet.
Die Schnittstellenzuordnung für eine Klasse oder Struktur C
sucht eine Implementierung für jedes Element jeder Schnittstelle, die in der Basisklassenliste von C
. Die Implementierung eines bestimmten Schnittstellenelements I.M
, wobei I
es sich um die Schnittstelle handelt, in der das Element M
deklariert wird, wird bestimmt, indem jede Klasse oder Struktur S
untersucht wird, beginnend mit C
und wiederholt für jede aufeinander folgende Basisklasse von C
, bis eine Übereinstimmung gefunden wird:
- Wenn
S
eine Deklaration einer expliziten Schnittstellenmitgliedsimplementierung enthält, die übereinstimmenI
undM
, ist dieses Element die Implementierung vonI.M
. - Andernfalls, wenn
S
eine Deklaration eines nicht statischen öffentlichen Mitglieds enthält, das übereinstimmtM
, ist dieses Element die Implementierung vonI.M
. Wenn mehrere Member übereinstimmen, wird nicht angegeben, welches Mitglied die Implementierung vonI.M
. Diese Situation kann nur auftreten, wennS
es sich um einen konstruierten Typ handelt, bei dem die beiden Elemente, wie im generischen Typ deklariert, unterschiedliche Signaturen aufweisen, aber die Typargumente machen ihre Signaturen identisch.
Ein Kompilierungsfehler tritt auf, wenn Implementierungen nicht für alle Member aller Schnittstellen gefunden werden können, die in der Basisklassenliste von C
. Die Member einer Schnittstelle enthalten die Elemente, die von Basisschnittstellen geerbt werden.
Elemente eines konstruierten Schnittstellentyps gelten als alle Typparameter, die durch die entsprechenden Typargumente ersetzt werden, wie in §15.3.3 angegeben.
Beispiel: Beispiel: Bei der generischen Schnittstellendeklaration:
interface I<T> { T F(int x, T[,] y); T this[int y] { get; } }
die konstruierte Schnittstelle
I<string[]>
hat die Elemente:string[] F(int x, string[,][] y); string[] this[int y] { get; }
Endbeispiel
Für Die Zwecke der Schnittstellenzuordnung entspricht ein Klassen- oder Strukturmemmemm A
einem Schnittstellenmemm B
, wenn:
A
undB
sind Methoden und die Namen-, Typ- und Parameterlisten vonA
undB
sind identisch.A
undB
sind Eigenschaften, der Name und typ vonA
undB
sind identisch undA
weisen dieselben Accessoren auf wieB
(A
ist zulässig, wenn es sich nicht um eine explizite Schnittstellenmememlerimplementierung handelt).A
undB
sind Ereignisse, und der Name und Typ vonA
undB
sind identisch.A
B
und sind Indexer, die Typ- undB
ParameterlistenA
und sind identisch undA
weisen dieselben Accessoren auf wieB
(A
ist zulässig, wenn es sich nicht um eine explizite Schnittstellenmememlerimplementierung handelt).
Wichtige Auswirkungen des Schnittstellenzuordnungsalgorithmus sind:
- Explizite Schnittstellenmemberimplementierungen haben Vorrang vor anderen Membern in derselben Klasse oder Struktur, wenn sie die Klasse oder strukturieren, die ein Schnittstellenmember implementiert.
- Weder nicht öffentlich noch statische Member nehmen an der Schnittstellenzuordnung teil.
Beispiel: Im folgenden Code
interface ICloneable { object Clone(); } class C : ICloneable { object ICloneable.Clone() {...} public object Clone() {...} }
das
ICloneable.Clone
MitgliedC
der ImplementierungClone
in "ICloneable" wird, da explizite Schnittstellenmemberimplementierungen Vorrang vor anderen Membern haben.Endbeispiel
Wenn eine Klasse oder Struktur zwei oder mehr Schnittstellen implementiert, die ein Element mit demselben Namen, Typ und Parametertypen enthalten, ist es möglich, jede dieser Schnittstellenmember einem einzelnen Klassen- oder Strukturmember zuzuordnen.
Beispiel:
interface IControl { void Paint(); } interface IForm { void Paint(); } class Page : IControl, IForm { public void Paint() {...} }
Hier werden die
Paint
Methoden von beidenIControl
undIForm
derPaint
Methode zugeordnet inPage
. Es ist natürlich auch möglich, separate explizite Schnittstellenmememerimplementierungen für die beiden Methoden zu haben.Endbeispiel
Wenn eine Klasse oder Struktur eine Schnittstelle implementiert, die ausgeblendete Member enthält, müssen einige Member möglicherweise über explizite Schnittstellenmemberimplementierungen implementiert werden.
Beispiel:
interface IBase { int P { get; } } interface IDerived : IBase { new int P(); }
Eine Implementierung dieser Schnittstelle erfordert mindestens eine explizite Schnittstellenmemmimplementierung und würde eine der folgenden Formen annehmen:
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() {...} }
Endbeispiel
Wenn eine Klasse mehrere Schnittstellen implementiert, die dieselbe Basisschnittstelle aufweisen, kann es nur eine Implementierung der Basisschnittstelle geben.
Beispiel: Im folgenden 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) {...} }
es ist nicht möglich, separate Implementierungen für die
IControl
benannte in der Basisklassenliste, dieIControl
geerbt vonITextBox
, und dieIControl
geerbt vonIListBox
. Tatsächlich gibt es keinen Begriff einer separaten Identität für diese Schnittstellen. Stattdessen werden die Implementierungen undITextBox
IListBox
die gleiche Implementierung vonIControl
, undComboBox
wird einfach als Implementierung von drei Schnittstellen,IControl
, ,ITextBox
undIListBox
.Endbeispiel
Die Member einer Basisklasse nehmen an der Schnittstellenzuordnung teil.
Beispiel: Im folgenden Code
interface Interface1 { void F(); } class Class1 { public void F() {} public void G() {} } class Class2 : Class1, Interface1 { public new void G() {} }
die Methode
F
inClass1
wird beiClass2's
der Implementierung vonInterface1
.Endbeispiel
18.6.6 Schnittstellenimplementierungsvererbung
Eine Klasse erbt alle Schnittstellenimplementierungen, die von ihren Basisklassen bereitgestellt werden.
Ohne explizite erneute Implementierung einer Schnittstelle kann eine abgeleitete Klasse die schnittstellenzuordnungen, die sie von ihren Basisklassen erbt, nicht ändern.
Beispiel: In den Deklarationen
interface IControl { void Paint(); } class Control : IControl { public void Paint() {...} } class TextBox : Control { public new void Paint() {...} }
die
Paint
Methode inTextBox
blendet diePaint
MethodeControl
in aus, ändert jedoch nicht die Zuordnung vonControl.Paint
"ontoIControl.Paint
", und Aufrufe vonPaint
Klasseninstanzen und Schnittstelleninstanzen haben die folgenden Auswirkungen: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 Control.Paint();
Endbeispiel
Wenn jedoch eine Schnittstellenmethode einer virtuellen Methode in einer Klasse zugeordnet wird, ist es möglich, abgeleitete Klassen die virtuelle Methode außer Kraft zu setzen und die Implementierung der Schnittstelle zu ändern.
Beispiel: Umschreiben der obigen Deklarationen in
interface IControl { void Paint(); } class Control : IControl { public virtual void Paint() {...} } class TextBox : Control { public override void Paint() {...} }
die folgenden Effekte werden nun beobachtet
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();
Endbeispiel
Da explizite Schnittstellenmememimplementierungen nicht virtuell deklariert werden können, ist es nicht möglich, eine explizite Schnittstellenmememerimplementierung außer Kraft zu setzen. Es ist jedoch perfekt gültig für eine explizite Schnittstellenmememimplementierung, um eine andere Methode aufzurufen, und dass andere Methode virtuell deklariert werden kann, damit abgeleitete Klassen sie außer Kraft setzen können.
Beispiel:
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() { PaintControl(); } protected virtual void PaintControl() {...} } class TextBox : Control { protected override void PaintControl() {...} }
Hier können von
Control
abgeleitete Klassen die ImplementierungIControl.Paint
durch Außerkraftsetzung derPaintControl
Methode spezialisiert werden.Endbeispiel
18.6.7 Neuimplementierung der Schnittstelle
Eine Klasse, die eine Schnittstellenimplementierung erbt , kann die Schnittstelle erneut implementieren , indem sie in die Basisklassenliste eingeschlossen wird.
Eine erneute Implementierung einer Schnittstelle folgt genau den gleichen Schnittstellenzuordnungsregeln wie eine anfängliche Implementierung einer Schnittstelle. Somit hat die geerbte Schnittstellenzuordnung keinerlei Auswirkungen auf die Schnittstellenzuordnung, die für die erneute Implementierung der Schnittstelle eingerichtet wurde.
Beispiel: In den Deklarationen
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() {...} } class MyControl : Control, IControl { public void Paint() {} }
die Tatsache, dass
Control
KartenIControl.Paint
zugeordnetControl.IControl.Paint
sind, wirkt sich nicht auf die Erneute Implementierung inMyControl
, die zugeordnetIControl.Paint
MyControl.Paint
wird.Endbeispiel
Geerbte Deklarationen öffentlicher Member und geerbte explizite Schnittstellenmemberdeklarationen nehmen an dem Schnittstellenzuordnungsprozess für neu implementierte Schnittstellen teil.
Beispiel:
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() {} }
Hier wird die Implementierung der
IMethods
Derived
Schnittstellenmethoden aufDerived.F
, ,Base.IMethods.G
,Derived.IMethods.H
undBase.I
.Endbeispiel
Wenn eine Klasse eine Schnittstelle implementiert, implementiert sie implizit auch alle Basisschnittstellen dieser Schnittstelle. Ebenso ist eine erneute Implementierung einer Schnittstelle implizit eine erneute Implementierung aller Basisschnittstellen der Schnittstelle.
Beispiel:
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() {...} }
Hier wird die neu implementierte Neuimplementierung
IDerived
IBase
, ZuordnungIBase.F
.D.F
Endbeispiel
18.6.8 Abstrakte Klassen und Schnittstellen
Wie eine nicht abstrakte Klasse muss eine abstrakte Klasse Implementierungen aller Member der Schnittstellen bereitstellen, die in der Basisklassenliste der Klasse aufgeführt sind. Eine abstrakte Klasse ist jedoch berechtigt, Schnittstellenmethoden abstrakten Methoden zuzuordnen.
Beispiel:
interface IMethods { void F(); void G(); } abstract class C : IMethods { public abstract void F(); public abstract void G(); }
Hier erfolgt die Implementierung von
IMethods
KartenF
undG
auf abstrakte Methoden, die in nicht abstrakten Klassen überschrieben werden sollen, die vonC
.Endbeispiel
Explizite Schnittstellenmememimplementierungen können nicht abstrahiert werden, aber explizite Schnittstellenmememimplementierungen dürfen natürlich abstrakte Methoden aufrufen.
Beispiel:
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(); }
Hier müssten nicht abstrakte Klassen, die von
C
der AußerkraftsetzungFF
abgeleitet werden, undGG
somit die tatsächliche Implementierung vonIMethods
.Endbeispiel
ECMA C# draft specification