Freigeben über


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, internalund 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, , internalund 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 und Z 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 Elementtyp
  • T ist eine Schnittstelle oder ein Delegattyp Sᵢ,... Aₑ , der aus einem generischen Typ S<Xᵢ, ... Xₑ> erstellt wird, wobei mindestens einer Aᵢ der folgenden Haltebereiche gilt:
    • Xᵢ ist kovariant oder invariant und Aᵢ ist ausgabeunsicher.
    • Xᵢ ist kontravariant oder invariant und Aᵢ ist eingabeunsicher.

Ein Typ T ist eingabeunsicher , wenn einer der folgenden Haltebereiche enthält:

  • T ist ein kovarianter Typparameter
  • T ist ein Arraytyp mit einem eingabeunsicheren Elementtyp
  • T ist eine Schnittstelle oder ein Delegattyp S<Aᵢ,... Aₑ> , der aus einem generischen Typ S<Xᵢ, ... Xₑ> erstellt wird, wobei mindestens einer Aᵢ der folgenden Haltebereiche gilt:
    • Xᵢ ist kovariant oder invariant und Aᵢ ist eingabeunsicher.
    • Xᵢ ist kontravariant oder invariant und Aᵢ 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 von Aᵢ zu Bᵢ
  • Xᵢ ist kontravariant und eine implizite Referenz- oder Identitätskonvertierung ist von Bᵢ zu Aᵢ
  • Xᵢ ist invariant und eine Identitätskonvertierung ist von Aᵢ zu Bᵢ

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 im internal interface_base einer public 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 sind IControl, ITextBoxund IListBox. Mit anderen Worten, die IComboBox schnittstelle oben erbt Member SetText und SetItems sowie Paint.

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 die Combine Methode, nachdem der Typparameter T durch string[,]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, und ref.
  • 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 Klasse object 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änkung U 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, dass E diese ableiten D, 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 in IListCounter mehrdeutig ist. Wie im Beispiel dargestellt, wird die Mehrdeutigkeit durch Umwandlung x 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ählt IInteger.Add aus, indem Überlastungsregeln von §12.6.4 angewendet werden. Ebenso wählt IDouble.Addder Aufruf n.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 ausgeblendet ILeft.F ist. Der Aufruf d.F(1) wählt ILeft.Fdaher aus, auch wenn IBase.F er nicht in dem Durchgangspfad verborgen erscheint, der durch IRightfü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 zu ILeft zu IBase ausblenden IBase.Fist, wird das Element auch im Zugriffspfad von IDerived zu " zu IRight " IBaseausgeblendet.

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 lautet ITextBox.SetText. Im obigen Beispiel ist es nicht möglich, auf Paint 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 auch ICloneable.Clone System.ICloneable.Clone qualifizierte Schnittstellenmememmnamen für die Clone 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 als ITextBoxauch IControl .

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 und IDictionary<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 die Dispose Methode der IDisposable 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, da IComparable sie nicht in der Basisklassenliste Shape aufgeführt ist und keine Basisschnittstelle von ICloneable. Ebenso in den Deklarationen

class Shape : ICloneable
{
    object ICloneable.Clone() {...}
}

class Ellipse : Shape
{
    object ICloneable.Clone() {...} // invalid
}

die Deklaration von ICloneable.Clone in Ellipse results in a compile-time error because ICloneable is not explicitly listed in the base class list of Ellipse.

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.Paintnicht ITextBox.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 Schnittstellendeklaration Cangegeben sind.
  • L Fügen Sie alle Basisschnittstellen der Schnittstellen hinzu, die bereits vorhanden Lsind.
  • Entfernen Sie alle Duplikate aus L.
  • Wenn ein möglicher konstruierter Typ, der erstellt C wurde, nach dem Ersetzen von Typargumenten in L, bewirkt, dass zwei Schnittstellen L identisch sind, dann ist die Deklaration C ungültig. Einschränkungsdeklarationen werden beim Ermitteln aller möglichen konstruierten Typen nicht berücksichtigt.

Hinweis: In der obigen Klassendeklaration X besteht die Schnittstellenliste L aus l<U> und I<V>. Die Deklaration ist ungültig, da jeder konstruierte Typ mit U und V 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 Derivedauf, 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 implementiert I<object,C,string>.F<T>. In diesem Fall ist es nicht erforderlich (oder zulässig), die Einschränkung T: object anzugeben, C.F<T> da object es sich um eine implizite Einschränkung für alle Typparameter handelt. Die Methode C.G<T> implementiert I<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 Methode C.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 implementieren I<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änkung T: stringerbt, 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 Suntersucht 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 übereinstimmen I und M, ist dieses Element die Implementierung von I.M.
  • Andernfalls, wenn S eine Deklaration eines nicht statischen öffentlichen Mitglieds enthält, das übereinstimmt M, ist dieses Element die Implementierung von I.M. Wenn mehrere Member übereinstimmen, wird nicht angegeben, welches Mitglied die Implementierung von I.M. Diese Situation kann nur auftreten, wenn S 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 und B sind Methoden und die Namen-, Typ- und Parameterlisten von A und B sind identisch.
  • A und B sind Eigenschaften, der Name und typ von A und B sind identisch und A weisen dieselben Accessoren auf wie B (A ist zulässig, wenn es sich nicht um eine explizite Schnittstellenmememlerimplementierung handelt).
  • A und B sind Ereignisse, und der Name und Typ von A und B sind identisch.
  • AB und sind Indexer, die Typ- und B Parameterlisten A und sind identisch und A weisen dieselben Accessoren auf wie B (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 Mitglied C der Implementierung Clone 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 beiden IControl und IForm der Paint Methode zugeordnet in Page. 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, die IControl geerbt von ITextBox, und die IControl geerbt von IListBox. Tatsächlich gibt es keinen Begriff einer separaten Identität für diese Schnittstellen. Stattdessen werden die Implementierungen und ITextBoxIListBox die gleiche Implementierung von IControl, und ComboBox wird einfach als Implementierung von drei Schnittstellen, IControl, , ITextBoxund IListBox.

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 in Class1 wird bei Class2's der Implementierung von Interface1.

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 in TextBox blendet die Paint Methode Controlin aus, ändert jedoch nicht die Zuordnung von Control.Paint "onto IControl.Paint", und Aufrufe von Paint 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 Implementierung IControl.Paint durch Außerkraftsetzung der PaintControl 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 Karten IControl.Paint zugeordnet Control.IControl.Paint sind, wirkt sich nicht auf die Erneute Implementierung in MyControl, die zugeordnet IControl.Paint MyControl.Paintwird.

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 auf Derived.F, , Base.IMethods.G, Derived.IMethods.Hund Base.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, Zuordnung IBase.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 Karten F und G auf abstrakte Methoden, die in nicht abstrakten Klassen überschrieben werden sollen, die von C.

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ßerkraftsetzung FF abgeleitet werden, und GGsomit die tatsächliche Implementierung von IMethods.

Endbeispiel