Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
19.1 Algemeen
Een interface definieert een contract. Een klasse of struct die een interface implementeert, voldoet aan het contract. Een interface kan overnemen van meerdere basisinterfaces en een klasse of struct kan meerdere interfaces implementeren.
Interfaces kunnen verschillende soorten leden bevatten, zoals beschreven in §19.4. De interface zelf kan een implementatie bieden voor sommige of alle functieleden die deze declareert. Leden waarvoor de interface geen implementatie biedt, zijn abstract. Hun implementaties moeten worden geleverd door klassen of structs die de interface of afgeleide interface implementeren die een onderdrukkingsdefinitie bieden.
Opmerking: Historisch gezien heeft het toevoegen van een nieuw functielid aan een interface invloed op alle bestaande gebruikers van dat interfacetype; het was een belangrijke wijziging. Dankzij de toevoeging van implementaties van interfacefuncties konden ontwikkelaars een interface upgraden terwijl alle implementors die implementatie overschrijven. Gebruikers van de interface kunnen de implementatie accepteren als een niet-belangrijke wijziging; Als hun vereisten echter afwijken, kunnen ze de opgegeven implementaties overschrijven. eindnotitie
19.2 Interfacedeclaraties
19.2.1 Algemeen
Een interface_declaration is een type_declaration (§14.7) die een nieuw interfacetype declareert.
interface_declaration
: attributes? interface_modifier* 'partial'? 'interface'
identifier variant_type_parameter_list? interface_base?
type_parameter_constraints_clause* interface_body ';'?
;
Een interface_declaration bestaat uit een optionele set kenmerken (§23), gevolgd door een optionele set interface_modifiers (§19.2.2), gevolgd door een optionele gedeeltelijke wijziging (§15.2.7), gevolgd door het trefwoord interface en een id die de interface een naam geeft, gevolgd door een optionele variant_type_parameter_list specificatie (§19.2.3), gevolgd door een optionele interface_base specificatie (§19.2.4), gevolgd door een optionele specificatie van type_parameter_constraints_clause(§15.2.5), gevolgd door een interface_body (§19.3), eventueel gevolgd door een puntkomma.
Een interfacedeclaratie levert geen type_parameter_constraints_clauses, tenzij deze ook een variant_type_parameter_listlevert.
Een interfacedeclaratie die een variant_type_parameter_list levert, is een algemene interfacedeclaratie. Bovendien is elke interface die is genest in een algemene klassedeclaratie of een algemene structdeclaratie zelf een algemene interfacedeclaratie, aangezien typeargumenten voor het betreffende type worden opgegeven om een samengesteld type te maken (§8.4).
19.2.2 Interface modifiers
Een interface_declaration kan eventueel een reeks interfaceaanpassingen bevatten:
interface_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§24.2) is alleen beschikbaar in onveilige code (§24).
Het is een compilatiefout voor dezelfde wijziging die meerdere keren wordt weergegeven in een interfacedeclaratie.
De new wijzigingsfunctie is alleen toegestaan op interfaces die binnen een klasse zijn gedefinieerd. Hiermee wordt aangegeven dat de interface een overgenomen lid met dezelfde naam verbergt, zoals beschreven in §15.3.5.
De public, protecteden internalprivate modifiers bepalen de toegankelijkheid van de interface. Afhankelijk van de context waarin de interfacedeclaratie plaatsvindt, zijn mogelijk slechts enkele van deze modifiers toegestaan (§7.5.2). Wanneer een gedeeltelijke typedeclaratie (§15.2.7) een toegankelijkheidsspecificatie (via de public, protected, internalen private modifiers) bevat, zijn de regels in §15.2.2 van toepassing.
Parameterlijsten voor varianttypen 19.2.3
19.2.3.1 Algemeen
Varianttype-parameterlijsten kunnen alleen voorkomen op interface- en delegaattypen. Het verschil met gewone type_parameter_lists is de optionele variance_annotation voor elke typeparameter.
variant_type_parameter_list
: '<' variant_type_parameter (',' variant_type_parameter)* '>'
;
variant_type_parameter
: attributes? variance_annotation? type_parameter
;
variance_annotation
: 'in'
| 'out'
;
Als de afwijkingsaantekening isout, wordt de typeparameter covariant genoemd. Als de afwijkingsaantekening isin, wordt de typeparameter als contravariant beschouwd. Als er geen afwijkingsaantekening is, wordt de typeparameter als invariant gezegd.
Voorbeeld: In het volgende:
interface C<out X, in Y, Z> { X M(Y y); Z P { get; set; } }
Xis covariant,Yis contravariant enZis invariant.eindvoorbeeld
Als een algemene interface in meerdere delen wordt gedeclareerd (§15.2.3), geeft elke gedeeltelijke aangifte dezelfde variantie op voor elke typeparameter.
19.2.3.2 Variantieveiligheid
Het optreden van afwijkingsannotaties in de lijst met typeparameters van een type beperkt de plaatsen waar typen kunnen optreden in de typedeclaratie.
Een type T is uitvoer onveilig als een van de volgende bewaringen geldt:
-
Tis een parameter voor het type contravariant -
Tis een arraytype met een uitvoer-onveilig elementtype -
Tis een interface of gedelegeerd typeSᵢ,... Aₑdat is samengesteld uit een generiek typeS<Xᵢ, ... Xₑ>, waarbij voor ten minste éénAᵢeen van de volgende voorwaarden geldt:-
Xᵢis covariant ofwel invariant enAᵢis uitvoer-onveilig. -
Xᵢis contravariant of invariant enAᵢis onveilig voor invoer.
-
Een type T is invoer onveilig als een van de volgende bewaringen geldt:
-
Tis een parameter voor het covarianttype -
Tis een matrixtype met een invoer onveilig elementtype -
Tis een interface of gedelegeerd typeS<Aᵢ,... Aₑ>dat is samengesteld uit een generiek typeS<Xᵢ, ... Xₑ>, waarbij voor ten minste éénAᵢeen van de volgende voorwaarden geldt:-
Xᵢis covariant of invariant enAᵢis onveilig voor invoer. -
Xᵢis contravariant of invariant enAᵢis uitvoer onveilig.
-
Intuïtief is een uitvoer onveilig type verboden in een uitvoerpositie en is een invoer onveilig type niet toegestaan in een invoerpositie.
Een type is uitvoerveilig als het niet uitvoer onveilig is, en invoerveilig als het niet invoer onveilig is.
19.2.3.3 Variantieconversie
Het doel van afwijkingsaantekeningen is om meer lenige (maar toch typeveilige) conversies naar interface- en gedelegeerdentypen te bieden. Daartoe maken de definities van impliciete (§10.2) en expliciete conversies (§10.3) gebruik van het begrip variantie-conversie, die als volgt wordt gedefinieerd:
Een type T<Aᵢ, ..., Aᵥ> is variantie-converteerbaar naar een type T<Bᵢ, ..., Bᵥ> als T een interface of een delegatetype is dat is gedeclareerd met varianttypeparameters T<Xᵢ, ..., Xᵥ>, en voor elke varianttypeparameter Xᵢ het volgende geldt:
-
Xᵢis covariant en er bestaat een impliciete verwijzing of identiteitsconversie vanAᵢnaarBᵢ -
Xᵢis contravariant en er bestaat een impliciete verwijzing of identiteitsconversie vanBᵢnaarAᵢ -
Xᵢis invariant en er bestaat een identiteitsconversie vanAᵢnaarBᵢ
19.2.4 Basisinterfaces
Een interface kan overnemen van nul of meer interfacetypen, die de expliciete basisinterfacesvan de interface worden genoemd. Wanneer een interface een of meer expliciete basisinterfaces heeft, wordt de interface-id gevolgd door een dubbele punt en een door komma's gescheiden lijst met basisinterfacetypen.
Een afgeleide interface kan nieuwe leden declareren die overgenomen leden verbergen (§7.7.2.3) die zijn gedeclareerd in basisinterfaces of expliciet overgenomen leden implementeren (§19.6.2) die zijn gedeclareerd in basisinterfaces.
interface_base
: ':' interface_type_list
;
De expliciete basisinterfaces kunnen worden samengesteld (§8.4, §19.2). Een basisinterface kan geen eigen typeparameter zijn, maar kan wel betrekking hebben op de typeparameters die binnen het bereik vallen.
Voor een samengesteld interfacetype worden de expliciete basisinterfaces gevormd door de expliciete basisinterfacedeclaraties op de algemene typedeclaratie te nemen en voor elke type_parameter in de basisinterfacedeclaratie de bijbehorende type_argument van het samengestelde type te vervangen.
De expliciete basisinterfaces van een interface moeten minstens zo toegankelijk zijn als de interface zelf (§7.5.5).
Opmerking: het is bijvoorbeeld een compilatiefout om een
privateofinternalinterface op te geven in de interface_base van eenpublicinterface. eindnotitie
Het is een compilatiefout voor een interface om direct of indirect van zichzelf over te nemen.
De basisinterfacesvan een interface zijn de expliciete basisinterfaces en de bijbehorende basisinterfaces. Met andere woorden, de set basisinterfaces vormt de volledige transitieve sluiting van de expliciete basisinterfaces en hun bijbehorende expliciete basisinterfaces, enzovoort. Een interface neemt alle leden van de basisinterfaces over.
Voorbeeld: In de volgende code
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } interface IListBox : IControl { void SetItems(string[] items); } interface IComboBox: ITextBox, IListBox {}de basisinterfaces
IComboBoxzijnIControl,ITextBoxenIListBox. Met andere woorden, de bovenstaandeIComboBox-interface erft ledenSetText,SetItemsen alsookPaint.eindvoorbeeld
Leden die zijn overgenomen van een samengesteld algemeen type, worden overgenomen na het vervangen van het type. Dat wil gezegd: alle samenstellende typen in het lid hebben de typeparameters van de basisklassedeclaratie vervangen door de bijbehorende typeargumenten die worden gebruikt in de specificatie van de class_base .
Voorbeeld: In de volgende code
interface IBase<T> { T[] Combine(T a, T b); } interface IDerived : IBase<string[,]> { // Inherited: string[][,] Combine(string[,] a, string[,] b); }de interface
IDerivedneemt deCombinemethode over nadat de typeparameterTis vervangen doorstring[,].eindvoorbeeld
Een klasse of struct waarmee een interface wordt geïmplementeerd, implementeert ook impliciet alle basisinterfaces van de interface.
De verwerking van interfaces op meerdere delen van een gedeeltelijke interfacedeclaratie (§15.2.7) wordt verder besproken in §15.2.4.3.
Elke basisinterface van een interface moet uitvoerveilig zijn (§19.2.3.2).
19.3 Interfacebody
De interface_body van een interface definieert de leden van de interface.
interface_body
: '{' interface_member_declaration* '}'
;
19.4 Interfaceleden
19.4.1 Algemeen
De leden van een interface zijn de leden die zijn overgenomen van de basisinterfaces en de leden die door de interface zelf zijn gedeclareerd.
interface_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| static_constructor_declaration
| operator_declaration
| type_declaration
;
Deze component vergroot de beschrijving van leden in klassen (§15.3) met beperkingen voor interfaces. De interfaceleden worden gedeclareerd met behulp van member_declaration smet de volgende aanvullende regels:
- Een finalizer_declaration is niet toegestaan.
- Exemplaarconstructors, constructor_declaration s, zijn niet toegestaan.
- Alle interfaceleden hebben impliciet openbare toegang; een expliciete wijzigingsfunctie voor toegang (§7.5.2) is echter toegestaan, behalve op statische constructors (§15.12).
- De
abstractmodifier wordt geïmpliceerd voor interfacefunctieleden zonder lichamen. Deze wijziging kan expliciet worden gegeven. - Een functielid van het interface-exemplaar waarvan de declaratie een hoofdtekst bevat, is impliciet
virtuallid, tenzij desealedofprivatewijzigingsfunctie wordt gebruikt. Devirtualwijzigingsfunctie kan expliciet worden gegeven. - Een
privateofsealedfunctielid van een interface heeft een hoofdtekst. - Een
privatefunctielid mag niet over de wijziging beschikkensealed. - Een afgeleide interface kan een abstract of virtueel lid overschrijven dat is gedeclareerd in een basisinterface.
- Een expliciet geïmplementeerd functielid mag niet over de wijziging beschikken
sealed.
Sommige declaraties, zoals constant_declaration (§15.4) hebben geen beperkingen in interfaces.
De overgenomen leden van een interface maken specifiek geen deel uit van de declaratieruimte van de interface. Een interface mag dus een lid met dezelfde naam of handtekening declareren als een overgenomen lid. Wanneer dit gebeurt, wordt gezegd dat het afgeleide interfacelid het lid van de basisinterface verbergt. Het verbergen van een overgenomen lid wordt niet beschouwd als een fout, maar leidt wel tot een waarschuwing (§7.7.2.3).
Als een new modifier is opgenomen in een declaratie die een overgenomen lid niet verbergt, wordt daarover een waarschuwing gegeven.
Opmerking: De leden in de klas
objectzijn niet strikt genomen leden van een interface (§19.4). De leden in de klasobjectzijn echter beschikbaar via het opzoeken van leden in elk interfacetype (§12.5). eindnotitie
De set leden van een interface die in meerdere delen is gedeclareerd (§15.2.7) is de vereniging van de leden die in elk deel zijn gedeclareerd. De organen van alle delen van de interfacedeclaratie delen dezelfde declaratieruimte (§7.3) en het bereik van elk lid (§7.7) strekt zich uit tot de lichamen van alle onderdelen.
Voorbeeld: Overweeg een interface
IAmet een implementatie voor een lidMen een eigenschapP. Een implementatietypeCbiedt geen implementatie voorMofP. Ze moeten worden geopend via een verwijzing waarvan het type compileertijd een interface is die impliciet converteerbaar is naarIAofIB. Deze leden worden niet gevonden via het opzoeken van leden op een variabele van het typeC.interface IA { public int P { get { return 10; } } public void M() { Console.WriteLine("IA.M"); } } interface IB : IA { public new int P { get { return 20; } } void IA.M() { Console.WriteLine("IB.M"); } } class C : IB { } class Test { public static void Main() { C c = new C(); ((IA)c).M(); // cast needed Console.WriteLine($"IA.P = {((IA)c).P}"); // cast needed Console.WriteLine($"IB.P = {((IB)c).P}"); // cast needed } }Binnen de interfaces
IAenIBis lidMrechtstreeks toegankelijk op naam. Binnen de methodeMainkunnen we echter niet schrijvenc.M()ofc.P, omdat deze namen niet zichtbaar zijn. Om ze te vinden, zijn casts naar het juiste interfacetype nodig. De declaratie van inMhet gebruik van expliciete syntaxis voorIBinterface-implementatie. Dit is noodzakelijk om ervoor te zorgen dat deze methode deIAmethode overschrijft; de modifieroverridemag niet worden toegepast op een functielid. eindvoorbeeld
19.4.2 Interfacevelden
Deze component vergroot de beschrijving van velden in klassen §15.5 voor velden die in interfaces zijn gedeclareerd.
Interfacevelden worden gedeclareerd met behulp van field_declarations (§15.5.1) met de volgende aanvullende regels:
- Het is een compilatietijdfout voor field_declaration om een exemplaarveld te declareren.
Voorbeeld: Het volgende programma bevat statische leden van verschillende soorten:
public interface IX { public const int Constant = 100; protected static int field; static IX() { Console.WriteLine("static members initialized"); Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}"); field = 50; Console.WriteLine("static constructor has run"); } } public class Test: IX { public static void Main() { Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}"); } }De geproduceerde uitvoer is
static members initialized constant = 100, field = 0 static constructor has run constant = 100, field = 50eindvoorbeeld
Zie §19.4.8 voor informatie over de toewijzing en initialisatie van statische velden.
19.4.3 Interfacemethoden
Deze component vergroot de beschrijving van methoden in klassen §15.6 voor methoden die zijn gedeclareerd in interfaces.
Interfacemethoden worden gedeclareerd met method_declarations (§15.6)). De kenmerken, return_type, ref_return_type, id en parameter_list van een interfacemethodedeclaratie hebben dezelfde betekenis als die van een methodedeclaratie in een klasse. Interfacemethoden hebben de volgende aanvullende regels:
method_modifier mag geen gegevens bevatten
override.Een methode waarvan de hoofdtekst een puntkomma is (
;) isabstract; deabstractmodifier is niet vereist, maar is toegestaan.Een interfacemethodedeclaratie met een bloktekst of expressietekst als een method_body is
virtual; devirtualmodifier is niet vereist, maar is toegestaan.Een method_declaration mag geen type_parameter_constraints_clausehebben, tenzij het ook een type_parameter_list heeft.
De lijst met vereisten voor geldige combinaties van modifiers die voor een klassemethode worden vermeld, wordt als volgt uitgebreid:
- Een statische declaratie die niet extern is, moet een bloktekst of expressietekst hebben als een method_body.
- Een virtuele declaratie die niet extern is, moet een bloktekst of expressietekst hebben als een method_body.
- Een persoonlijke verklaring die niet extern is, heeft als method_body een bloktekst of expressietekst.
- Een verzegelde verklaring die niet extern is, heeft als method_body een bloktekst of expressietekst.
- Een asynchrone declaratie heeft een bloktekst of expressietekst als method_body.
Alle parametertypen van een interfacemethode zijn invoerveilig (§19.2.3.2) en het retourtype moet veilig
voidzijn of uitvoerveilig zijn.Alle typen uitvoer- of verwijzingsparameters moeten ook veilig zijn.
Opmerking: uitvoerparameters moeten invoerveilig zijn vanwege algemene implementatiebeperkingen. eindnotitie
Elke beperking van het klassetype, de beperking van het interfacetype en de parameterbeperking van het type voor elk type van de methode is invoerveilig.
Deze regels zorgen ervoor dat elk covariant- of contravariantgebruik van de interface typesafe blijft.
Voorbeeld:
interface I<out T> { void M<U>() where U : T; // Error }is ongeldig omdat het gebruik van
Tals typeparameterbeperking opUniet invoerveilig is.Als deze beperking niet is ingesteld, zou het mogelijk zijn om de veiligheid van het type op de volgende manier te schenden:
interface I<out T> { void M<U>() where U : T; } class B {} class D : B {} class E : B {} class C : I<D> { public void M<D>() {...} } ... I<B> b = new C(); b.M<E>();Dit is eigenlijk een aanroep naar
C.M<E>. Maar die aanroep vereist datEafgeleid is vanD, zodat typeveiligheid hier wordt geschonden.eindvoorbeeld
Opmerking: Zie §19.4.2 voor een voorbeeld waarin niet alleen een statische methode met een implementatie wordt weergegeven, maar zoals deze methode wordt aangeroepen
Mainen het juiste retourtype en de juiste handtekening heeft, is het ook een ingangspunt. eindnotitie
Een virtuele methode met implementatie die in een interface is gedeclareerd, kan worden overschreven om abstract te zijn in een afgeleide interface. Dit staat bekend als reabstraction.
Voorbeeld:
interface IA { void M() { Console.WriteLine("IA.M"); } } interface IB: IA { abstract void IA.M(); // reabstraction of M }Dit is handig in afgeleide interfaces waarbij de implementatie van een methode ongepast is en een meer geschikte implementatie moet worden geboden door de implementatieklassen. eindvoorbeeld
19.4.4 Interface-eigenschappen
Deze component vergroot de beschrijving van eigenschappen in klassen §15.7 voor eigenschappen die in interfaces zijn gedeclareerd.
Interface-eigenschappen worden gedeclareerd met behulp van property_declarations (§15.7.1) met de volgende aanvullende regels:
property_modifier mag geen gegevens bevatten
override.Een expliciete implementatie van interfaceleden mag geen accessor_modifier bevatten (§15.7.3).
Een afgeleide interface kan expliciet een abstracte interface-eigenschap implementeren die is gedeclareerd in een basisinterface.
Opmerking: als een interface geen exemplaarvelden kan bevatten, kan een interface-eigenschap geen automatische instantieeigenschap zijn, omdat hiervoor de declaratie van impliciete verborgen exemplaarvelden vereist is. eindnotitie
Het type interface-eigenschap moet uitvoerveilig zijn als er een get accessor is en moet invoerveilig zijn als er een set accessor is.
Een interfacemethodedeclaratie met een bloktekst of expressietekst als een method_body is
virtual; devirtualmodifier is niet vereist, maar is toegestaan.Een exemplaar property_declaration zonder implementatie is
abstract; deabstractwijzigingsfunctie is niet vereist, maar is toegestaan. Het wordt nooit beschouwd als een automatisch geïmplementeerde eigenschap (§15.7.4).
19.4.5 Interface-gebeurtenissen
Deze component vergroot de beschrijving van gebeurtenissen in klassen §15.8 voor gebeurtenissen die zijn gedeclareerd in interfaces.
Interface-gebeurtenissen worden gedeclareerd met behulp van event_declarations (§15.8.1), met de volgende aanvullende regels:
-
event_modifier mag geen gegevens bevatten
override. - Een afgeleide interface kan een abstracte interfacegebeurtenis implementeren die is gedeclareerd in een basisinterface (§15.8.5).
- Het is een compilatiefout voor variable_declarators in een exemplaar event_declaration om variable_initializers te bevatten.
- Een instantie-gebeurtenis met de
virtualofsealedmodifiers moet accessors declareren. Het wordt nooit beschouwd als een automatisch geïmplementeerd veldachtige gebeurtenis (§15.8.2). - Een instantie-gebeurtenis met de
abstractwijzigingsfunctie mag geen toegangsrechten declareren. - Het type interfacegebeurtenis moet invoerveilig zijn.
19.4.6 Interface-indexeerfuncties
Deze component vergroot de beschrijving van indexeerfuncties in klassen §15,9 voor indexeerfuncties die in interfaces zijn gedeclareerd.
Interface-indexeerfuncties worden gedeclareerd met behulp van indexer_declarations (§15.9), met de volgende aanvullende regels:
indexer_modifier mag geen gegevens bevatten
override.Een indexer_declaration met een expressietekst of een accessor met een bloktekst of expressietekst is
virtual; devirtualwijziging is niet vereist, maar is toegestaan.Een indexer_declaration waarvan de toegangsorganen puntkomma's zijn (
;) isabstract; deabstractmodifier is niet vereist, maar is toegestaan.Alle parametertypen van een interface-indexeerfunctie moeten invoerveilig zijn (§19.2.3.2).
Alle typen uitvoer- of verwijzingsparameters moeten ook veilig zijn.
Opmerking: uitvoerparameters moeten invoerveilig zijn vanwege algemene implementatiebeperkingen. eindnotitie
Het type interface-indexeerder moet uitvoerveilig zijn als er een get-accessor is, en invoerveilig als er een set-accessor is.
19.4.7 Interfaceoperators
Deze component vergroot de beschrijving van operator_declaration leden in klassen §15.10 voor operators die in interfaces zijn gedeclareerd.
Een operator_declaration in een interface is de implementatie (§19.1).
Het is een compilatiefout voor een interface om een operator voor conversie, gelijkheid of ongelijkheid te declareren.
19.4.8 Interface statische constructors
Deze component vergroot de beschrijving van statische constructors in klassen §15.12 voor statische constructors die in interfaces zijn gedeclareerd.
De statische constructor voor een gesloten interface (§8.4.3) wordt maximaal één keer uitgevoerd in een bepaald toepassingsdomein. De uitvoering van een statische constructor wordt geactiveerd door de eerste van de volgende acties die moeten worden uitgevoerd binnen een toepassingsdomein:
- Naar een van de statische leden van de interface wordt verwezen.
- Voordat de
Mainmethode wordt aangeroepen voor een interface met deMainmethode (§7.1) waarin de uitvoering begint. - Deze interface biedt een implementatie voor een lid en die implementatie wordt gebruikt als de meest specifieke implementatie (§19.4.10) voor dat lid.
Opmerking: in het geval dat geen van de voorgaande acties plaatsvindt, kan de statische constructor voor een interface niet worden uitgevoerd voor een programma waarin exemplaren van typen die de interface implementeren, worden gemaakt en gebruikt. eindnotitie
Als u een nieuw type gesloten interface wilt initialiseren, wordt eerst een nieuwe set statische velden voor dat specifieke gesloten type gemaakt. Elk van de statische velden wordt geïnitialiseerd tot de standaardwaarde. Vervolgens worden de initialisaties van statische velden uitgevoerd voor deze statische velden. Ten slotte wordt de statische constructor uitgevoerd.
Opmerking: Zie §19.4.2 voor een voorbeeld van het gebruik van verschillende soorten statische leden (inclusief een Main-methode) die in een interface zijn gedeclareerd. eindnotitie
19.4.9 Interface geneste typen interface
Deze component vergroot de beschrijving van geneste typen in klassen §15.3.9 voor geneste typen die zijn gedeclareerd in interfaces.
Het is een fout om een klassetype, structtype of enumtype te declareren binnen het bereik van een typeparameter die is gedeclareerd met een variance_annotation (§19.2.3.1).
Voorbeeld: De onderstaande declaratie
Cis een fout.interface IOuter<out T> { class C { } // error: class declaration within scope of variant type parameter 'T' }eindvoorbeeld
19.4.10 meest specifieke implementatie
Elke klasse en struct hebben een meest specifieke implementatie voor elk virtueel lid dat is gedeclareerd in alle interfaces die door dat type zijn geïmplementeerd onder de implementaties die worden weergegeven in het type of de directe en indirecte interfaces. De meest specifieke implementatie is een unieke implementatie die specifieker is dan elke andere implementatie.
Opmerking: De meest specifieke implementatieregel zorgt ervoor dat een dubbelzinnigheid die voortvloeit uit de overname van diamantinterfaces expliciet wordt opgelost door de programmeur op het punt waar het conflict zich voordoet. eindnotitie
Voor een type T dat een struct of een klasse is die interfaces I2 implementeert en I3, waarbij en I2 beide I3 rechtstreeks of indirect worden afgeleid van de interface I die een lid Mdeclareert , is de meest specifieke implementatie vanM:
- Als
Teen implementatie wordtI.Mverklaard, is die implementatie de meest specifieke implementatie. -
TAls dit niet een klasse is en een directe of indirecte basisklasse een implementatie declareertI.M, is de meest afgeleide basisklasseTvan de meest specifieke implementatie. - Als en
I2als interfaces worden geïmplementeerd doorI3enTzijn afgeleid vanI3direct of indirect,I2is een specifiekere implementatie danI3.M.I2.M - Anders, noch
I2.MI3.Mzijn ze specifieker en treedt er een fout op.
Voorbeeld:
interface IA { void M() { Console.WriteLine("IA.M"); } } interface IB : IA { void IA.M() { Console.WriteLine("IB.M"); } } interface IC: IA { void IA.M() { Console.WriteLine("IC.M"); } } abstract class C: IB, IC { } // error: no most specific implementation for 'IA.M' abstract class D: IA, IB, IC // OK { public abstract void M(); }De meest specifieke implementatieregel zorgt ervoor dat een conflict (d.w. een dubbelzinnigheid die voortvloeit uit diamantovername) expliciet wordt opgelost door de programmeur op het punt waar het conflict zich voordoet. eindvoorbeeld
19.4.11 Toegang tot interfacelid
Interfaceleden worden geopend via lidtoegang (§12.8.7) en indexeerfunctietoegang (§12.8.12.4) expressies van het formulier I.M en I[A], waarbij I een interfacetype is, M een constante, veld, methode, eigenschap of gebeurtenis van dat interfacetype is en A een lijst met indexeerfuncties is.
In een klasse D, met directe of indirecte basisklasse B, waarbij B de interface I direct of indirect wordt geïmplementeerd en I een methode M()definieert, is de expressie base.M() alleen geldig als base.M() statisch (§12.3) is gebonden aan een implementatie van M() een klassetype.
Voor interfaces die strikt één overname zijn (elke interface in de overnameketen heeft exact nul of één directe basisinterface), de effecten van het opzoeken van leden (§12.5), methode-aanroep (§12.8.1 0.2) en indexeertoegangsregels (§12.8.12.4) zijn precies hetzelfde als voor klassen en structs: Meer afgeleide leden verbergen minder afgeleide leden met dezelfde naam of handtekening. Voor interfaces met meervoudige overerving kunnen er dubbelzinnigheden ontstaan wanneer twee of meer niet-gerelateerde basisinterfaces leden met eenzelfde naam of signatuur declareren. Deze subclause toont verschillende voorbeelden, waarvan sommige leiden tot dubbelzinnigheid en andere die niet. In alle gevallen kunnen expliciete casts worden gebruikt om de ambiguïteiten op te lossen.
Voorbeeld: In de volgende code
interface IList { int Count { get; set; } } interface ICounter { int Count { get; set; } } interface IListCounter : IList, ICounter {} class C { void Test(IListCounter x) { x.Count = 1; // Error ((IList)x).Count = 1; // Ok, invokes IList.Count.set ((ICounter)x).Count = 1; // Ok, invokes ICounter.Count } }de eerste instructie veroorzaakt een compilatiefout omdat de ledenzoekopdracht (§12.5) van
CountinIListCounterambigu is. Zoals geïllustreerd in het voorbeeld, wordt de dubbelzinnigheid opgelost door naar het juiste type basisinterface te castenx. Dergelijke casts hebben geen runtimekosten. Ze bestaan alleen uit het weergeven van het exemplaar als een minder afgeleid type tijdens het compileren.eindvoorbeeld
Voorbeeld: In de volgende 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 } }de aanroep
n.Add(1)selecteertIInteger.Adddoor overbelastingsregels van §12.6.4 toe te passen. Op dezelfde manier selecteert de aanroepn.Add(1.0)IDouble.Add. Wanneer expliciete casts worden ingevoegd, is er slechts één kandidaatmethode en daardoor ontstaat er geen dubbelzinnigheid.eindvoorbeeld
Voorbeeld: In de volgende 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 } }het
IBase.Flid is verborgen door hetILeft.Flid. De aanroepd.F(1)selecteertILeft.F, zelfs alsIBase.Fniet verborgen lijkt te zijn in het toegangspad dat viaIRightloopt.De intuïtieve regel voor verbergen in meervoudige overervingsinterfaces is eenvoudigweg het volgende: als een element verborgen is in een toegangspad, wordt het verborgen in alle toegangspaden. Omdat het toegangspad van
IDerivednaarILeftnaarIBaseIBase.Fverbergt, wordt het lid ook verborgen in het toegangspad vanIDerivednaarIRightnaarIBase.eindvoorbeeld
19.5 Gekwalificeerde interfacelidnamen
Een interfacelid wordt soms aangeduid met de naam van het gekwalificeerde interfacelid. De gekwalificeerde naam van een interfacelid bestaat uit de naam van de interface waarin het lid wordt gedeclareerd, gevolgd door een punt, gevolgd door de naam van het lid. De gekwalificeerde naam van een lid verwijst naar de interface waarin het lid wordt gedeclareerd.
Voorbeeld: Gegeven de declaraties
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); }de gekwalificeerde naam
PaintisIControl.Painten de gekwalificeerde naam van SetText isITextBox.SetText. In het bovenstaande voorbeeld is het niet mogelijk om te verwijzen naarPaintITextBox.Paint.eindvoorbeeld
Wanneer een interface deel uitmaakt van een naamruimte, kan een naam van een lid van de gekwalificeerde interface de naamruimtenaam bevatten.
Voorbeeld:
namespace System { public interface ICloneable { object Clone(); } }Binnen de
Systemnaamruimte zijn beideICloneable.CloneenSystem.ICloneable.Clonegekwalificeerde interfacelidnamen voor deClonemethode.eindvoorbeeld
19.6 Interface-implementaties
19.6.1 Algemeen
Interfaces kunnen worden geïmplementeerd door klassen en structs. Om aan te geven dat een klasse of struct rechtstreeks een interface implementeert, wordt de interface opgenomen in de lijst met basisklassen van de klasse of struct.
Een klasse of struct C die een interface I implementeert, moet een implementatie bieden of overnemen voor elk lid dat is gedeclareerd in I die C toegang heeft. Openbare leden van I kunnen worden gedefinieerd in openbare leden van C. Niet-openbare leden die zijn gedeclareerd I in die toegankelijk C zijn, kunnen worden gedefinieerd in C het gebruik van expliciete interface-implementatie (§19.6.2).
Een lid in een afgeleid type dat voldoet aan interfacetoewijzing (§19.6.5), maar het overeenkomende lid van de basisinterface implementeert geen nieuw lid. Dit gebeurt wanneer expliciete interface-implementatie vereist is om het interfacelid te definiëren.
Voorbeeld:
interface ICloneable { object Clone(); } interface IComparable { int CompareTo(object other); } class ListEntry : ICloneable, IComparable { public object Clone() {...} public int CompareTo(object other) {...} }eindvoorbeeld
Een klasse of struct die rechtstreeks een interface implementeert, implementeert ook impliciet alle basisinterfaces van de interface. Dit geldt zelfs als de klasse of struct niet expliciet alle basisinterfaces in de lijst met basisklassen weergeeft.
Voorbeeld:
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { public void Paint() {...} public void SetText(string text) {...} }Hier implementeert de klasse
TextBoxzowelIControlalsITextBox.eindvoorbeeld
Wanneer een klasse C rechtstreeks een interface implementeert, worden alle klassen die zijn afgeleid van C de interface ook impliciet geïmplementeerd.
De basisinterfaces die zijn opgegeven in een klassedeclaratie kunnen worden samengesteld uit interfacetypen (§8.4, §19.2).
Voorbeeld: De volgende code illustreert hoe een klasse samengestelde interfacetypen kan implementeren:
class C<U, V> {} interface I1<V> {} class D : C<string, int>, I1<string> {} class E<T> : C<int, T>, I1<T> {}eindvoorbeeld
De basisinterfaces van een algemene klassedeclaratie voldoen aan de in §19.6.3 beschreven uniekheidsregel.
19.6.2 Implementaties van expliciet interfacelid
Voor het implementeren van interfaces kan een klasse, struct of interface expliciete implementaties van interfaceledendeclareren. Een expliciete implementatie van interfaceleden is een methode, eigenschap, gebeurtenis of indexeerfunctiedeclaratie die verwijst naar de naam van een gekwalificeerde interfacelid. Een klasse of struct die een niet-openbaar lid in een basisinterface implementeert, moet een expliciete implementatie van interfaceleden declareren. Een interface die een lid in een basisinterface implementeert, moet een expliciete implementatie van een interfacelid declareren.
Een afgeleide interfacelid dat voldoet aan interfacetoewijzing (§19.6.5) verbergt het lid van de basisinterface (§7.7.2). De compiler geeft een waarschuwing tenzij de new wijzigingsfunctie aanwezig is.
Voorbeeld:
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>.thisenIDictionary<int,T>.Addzijn expliciete implementaties van interfaceleden.eindvoorbeeld
Voorbeeld: In sommige gevallen is de naam van een interfacelid mogelijk niet geschikt voor de implementatieklasse. In dat geval kan het interfacelid worden geïmplementeerd met behulp van expliciete implementatie van interfaceleden. Een klasse die bijvoorbeeld een bestandsabstractie implementeert, zou waarschijnlijk een
Closelidfunctie implementeren die het effect heeft van het vrijgeven van de bestandsresource en deDisposemethode van deIDisposableinterface implementeren met behulp van expliciete implementatie van interfaceleden: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); } }eindvoorbeeld
Het is niet mogelijk om toegang te krijgen tot een expliciete implementatie van een interfacelid via de naam van het gekwalificeerde interfacelid in een methodeaanroep, eigenschapstoegang, gebeurtenistoegang of indexeertoegang. Een expliciete implementatie van het interface-exemplaarlid kan alleen worden geopend via een interface-exemplaar en wordt in dat geval gewoon verwezen naar de naam van het lid. Een expliciete implementatie van statisch lid kan alleen worden geopend via de interfacenaam.
Het is een compilatiefout voor een expliciete implementatie van interfaceleden om andere modifiers (§15.6) dan extern of asyncop te nemen.
Een expliciete implementatie van de interfacemethode neemt eventuele typeparameterbeperkingen over van de interface.
Een type_parameter_constraints_clause op een expliciete interfacemethode-implementatie mag alleen bestaan uit de class of structprimary_constraintdie zijn toegepast op type_parameters die bekend zijn volgens de overgenomen beperkingen die respectievelijk verwijzings- of waardetypen zijn. Elk type van het formulier T? in de handtekening van de expliciete interfacemethode-implementatie, waarbij T een typeparameter is, wordt als volgt geïnterpreteerd:
- Als er een beperking wordt toegevoegd voor de
classtypeparameterT,T?is dit een null-verwijzingstype; anders - Als er geen toegevoegde beperking is, of als er een
structbeperking wordt toegevoegd, dan isTvoor de typeparameterT?een nullable-waardetype.
Voorbeeld: Hieronder ziet u hoe de regels werken wanneer typeparameters betrokken zijn:
#nullable enable interface I { void Foo<T>(T? value) where T : class; void Foo<T>(T? value) where T : struct; } class C : I { void I.Foo<T>(T? value) where T : class { } void I.Foo<T>(T? value) where T : struct { } }Zonder de typeparameter beperking
where T : classkan de basismethode met de referentietype parameter niet worden overschreven. eindvoorbeeld
Opmerking: Expliciete implementaties van interfaceleden hebben verschillende toegankelijkheidskenmerken dan andere leden. Omdat expliciete implementaties van interfaceleden nooit toegankelijk zijn via een gekwalificeerde naam van een interfacelid in een methodeaanroep of toegang tot een eigenschap, zijn ze in zekere zin privé. Omdat ze echter toegankelijk zijn via de interface, zijn ze in zekere zin ook zo openbaar als de interface waarin ze worden gedeclareerd. Expliciete implementaties van interfaceleden dienen twee primaire doeleinden:
- Omdat expliciete implementaties van interfaceleden niet toegankelijk zijn via klasse- of struct-exemplaren, kunnen interface-implementaties worden uitgesloten van de openbare interface van een klasse of struct. Dit is met name handig wanneer een klasse of struct een interne interface implementeert die niet van belang is voor een consument van die klasse of struct.
- Expliciete implementaties van interfaceleden maken ondubbelzinnigheid mogelijk van interfaceleden met dezelfde handtekening. Zonder expliciete implementaties van interfaceleden zou het onmogelijk zijn voor een klasse, struct of interface om verschillende implementaties van interfaceleden met dezelfde handtekening en retourtype te hebben, net zoals het onmogelijk zou zijn voor een klasse, struct of interface om een implementatie te hebben voor alle interfaceleden met dezelfde handtekening, maar met verschillende retourtypen.
eindnotitie
Voor een expliciete implementatie van interfaceleden moet de klasse, struct of interface een interface een naam geven in de basisklasse of de lijst met basisinterfaces die een lid bevat waarvan de naam van het gekwalificeerde interfacelid, het type, het aantal parameters en parametertypen exact overeenkomen met die van de expliciete implementatie van interfaceleden. Als een lid van een interfacefunctie een parametermatrix heeft, is de bijbehorende parameter van een gekoppelde expliciete implementatie van interfaceleden toegestaan, maar niet vereist, om de params wijzigingsfunctie te hebben. Als het lid van de interfacefunctie geen parametermatrix heeft, heeft een gekoppelde expliciete implementatie van interfaceleden geen parametermatrix.
Voorbeeld: Dus in de volgende klasse
class Shape : ICloneable { object ICloneable.Clone() {...} int IComparable.CompareTo(object other) {...} // invalid }de declaratie van
IComparable.CompareToresulteert in een compilatietijdfout omdatIComparableniet wordt vermeld in de basisklasselijst vanShapeen geen basisinterface is vanICloneable. Zoals in de declaratiesclass Shape : ICloneable { object ICloneable.Clone() {...} } class Ellipse : Shape { object ICloneable.Clone() {...} // invalid }De declaratie van
ICloneable.CloneinEllipseresulteert in een compileertijdsfout omdatICloneableniet expliciet wordt vermeld in de lijst met basisklassen vanEllipse.eindvoorbeeld
De naam van het gekwalificeerde interfacelid van een expliciete implementatie van interfaceleden verwijst naar de interface waarin het lid is gedeclareerd.
Voorbeeld: Dus in de declaraties
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { void IControl.Paint() {...} void ITextBox.SetText(string text) {...} }de expliciete implementatie van het interfacelid van Paint moet worden geschreven als
IControl.Paint, nietITextBox.Paint.eindvoorbeeld
19.6.3 Uniekheid van geïmplementeerde interfaces
De interfaces die door een algemene typedeclaratie worden geïmplementeerd, blijven uniek voor alle mogelijke samengestelde typen. Zonder deze regel zou het onmogelijk zijn om de juiste methode te bepalen die moet worden aangeroepen voor bepaalde samengestelde typen.
Voorbeeld: Stel dat een algemene klassedeclaratie als volgt mag worden geschreven:
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() {...} }Als dit is toegestaan, zou het onmogelijk zijn om te bepalen welke code in het volgende geval moet worden uitgevoerd:
I<int> x = new X<int, int>(); x.F();eindvoorbeeld
Om te bepalen of de interfacelijst van een algemene typedeclaratie geldig is, worden de volgende stappen uitgevoerd:
- Laten we
Lde lijst met interfaces zijn die rechtstreeks zijn opgegeven in een algemene klasse, struct of interfacedeclaratieC. - Voeg toe aan
Lde basisinterfaces van de interfaces die al inLaanwezig zijn. - Verwijder eventuele duplicaten uit
L. - Als een mogelijk samengesteld type dat is gemaakt van
Cna vervanging van typeargumenten inLervoor zou zorgen dat twee interfaces inLidentiek zijn, dan is de declaratie vanCongeldig. Declaraties van beperkingen worden niet overwogen bij het bepalen van alle mogelijke samengestelde typen.
Opmerking: In de bovenstaande klassedeclaratie
Xbestaat de interfacelijstLuitl<U>enI<V>. De declaratie is ongeldig omdat een geconstrueerd type waarbijUenVhetzelfde type zijn, zou betekenen dat deze twee interfaces identieke typen zijn. eindnotitie
Het is mogelijk voor interfaces die zijn opgegeven op verschillende overnameniveaus om het volgende te samenvoegen:
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() {...}
}
Deze code is geldig, ook al implementeert Derived<U,V> zowel I<U> als I<V>. De code
I<int> x = new Derived<int, int>();
x.F();
roept de methode aan in Derived, aangezien Derived<int,int>' deze effectief opnieuw wordt geïmplementeerd I<int> (§19.6.7).
19.6.4 Implementatie van algemene methoden
Wanneer een algemene methode impliciet een interfacemethode implementeert, moeten de beperkingen voor elke parameter van het methodetype equivalent zijn in beide declaraties (nadat parameters van het interfacetype worden vervangen door de juiste typeargumenten), waarbij parameters van het methodetype worden geïdentificeerd door rangtelposities, van links naar rechts.
Voorbeeld: In de volgende 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 }de methode
C.F<T>impliciet implementeertI<object,C,string>.F<T>. In dit gevalC.F<T>is niet vereist (noch toegestaan) om de beperkingT: objectop te geven, omdatobjectdit een impliciete beperking is voor alle typeparameters. De methodeC.G<T>implementeertI<object,C,string>.G<T>impliciet omdat de beperkingen overeenkomen met die in de interface, nadat de parameters van het interfacetype zijn vervangen door de bijbehorende typeargumenten. De beperking voor de methodeC.H<T>is een fout omdat verzegelde typen (stringin dit geval) niet als beperkingen kunnen worden gebruikt. Het weglaten van de beperking zou ook een fout zijn omdat beperkingen van impliciete interfacemethode-implementaties vereist zijn om overeen te komen. Het is dus onmogelijk impliciet te implementerenI<object,C,string>.H<T>. Deze interfacemethode kan alleen worden geïmplementeerd met behulp van een expliciete implementatie van interfaceleden: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 dit geval roept de expliciete implementatie van interfaceleden een openbare methode aan met strikt zwakkere beperkingen. De toewijzing van t naar s is geldig omdat
Teen beperking vanT: stringerft, ook al is deze beperking niet uitdrukbaar in de broncode. eindvoorbeeld
Opmerking: Wanneer een algemene methode expliciet een interfacemethode implementeert, zijn er geen beperkingen toegestaan voor de implementatiemethode (§15.7.1, §19.6.2). eindnotitie
19.6.5 Interfacetoewijzing
Een klasse of struct biedt implementaties van alle abstracte leden van de interfaces die worden vermeld in de lijst met basisklassen van de klasse of struct. Het proces voor het vinden van implementaties van interfaceleden in een implementatieklasse of struct wordt interfacetoewijzing genoemd.
Interfacetoewijzing voor een klasse of struct C zoekt een implementatie voor elk lid van elke interface die is opgegeven in de basisklasselijst van C. De implementatie van een bepaald interfacelid I.M, waarbij I de interface waarin het lid M wordt gedeclareerd, wordt bepaald door elke klasse, interface of struct Ste onderzoeken, te beginnen met C en te herhalen voor elke opeenvolgende basisklasse en geïmplementeerde interface van C, totdat een overeenkomst zich bevindt:
- Als
Seen declaratie bevat van een expliciete interfacelid-implementatie die overeenkomt metIenM, dan is dit lid de implementatie vanI.M. - Als
Sdaarentegen een declaratie bevat van een niet-statisch openbaar lid dat overeenkomt metM, dan is dit lid de implementatie vanI.M. Als er meer dan één lid overeenkomt, wordt niet opgegeven van welk lid de implementatieI.Mis. Deze situatie kan alleen optreden alsSeen samengesteld type is waarbij de twee leden die in het algemene type zijn gedeclareerd, verschillende handtekeningen hebben, maar de typeargumenten hun handtekeningen identiek maken.
Er treedt een compilatiefout op als implementaties niet kunnen worden gevonden voor alle leden van alle interfaces die zijn opgegeven in de basisklasselijst van C. De leden van een interface omvatten de leden die worden overgenomen van basisinterfaces.
Leden van een samengesteld interfacetype worden beschouwd als typen parameters vervangen door de bijbehorende typeargumenten zoals opgegeven in §15.3.3.
Voorbeeld: Bijvoorbeeld, op basis van de algemene interfacedeclaratie:
interface I<T> { T F(int x, T[,] y); T this[int y] { get; } }de samengestelde interface
I<string[]>heeft de leden:string[] F(int x, string[,][] y); string[] this[int y] { get; }eindvoorbeeld
Voor interfacetoewijzing komt een klasse, interface of struct-lid overeen met een interfacelid AB wanneer:
-
AenBzijn methoden, en de naam, het type en de parameterlijsten vanAenBzijn identiek. -
AenBeigenschappen zijn, de naam en het typeAenBidentiek zijn enAhebben dezelfde toegangsrechten alsB(Ais toegestaan om extra toegangsrechten te hebben als het geen expliciete implementatie van interfaceleden is). -
AenBgebeurtenissen zijn, en de naam en het typeAenBzijn identiek. -
AenBzijn indexeerfuncties, het type en de parameterlijsten vanAenBzijn identiek enAhebben dezelfde toegangsrechten alsB(Ais toegestaan om extra toegangsors te hebben als het geen expliciete implementatie van interfaceleden is).
Belangrijke gevolgen van het interfacetoewijzingsalgoritme zijn:
- Expliciete implementaties van interfaceleden hebben voorrang op andere leden in dezelfde klasse of struct bij het bepalen van de klasse of het struct-lid dat een interfacelid implementeert.
- Niet-openbare of statische leden nemen geen deel aan interfacetoewijzing.
Voorbeeld: In de volgende code
interface ICloneable { object Clone(); } class C : ICloneable { object ICloneable.Clone() {...} public object Clone() {...} }Het
ICloneable.Clone-lid vanCwordt de implementatie vanCloneinICloneableomdat expliciete implementaties van interfaceleden prioriteit hebben boven andere leden.eindvoorbeeld
Als een klasse of struct twee of meer interfaces implementeert die een lid met dezelfde naam, hetzelfde type en dezelfde parametertypen bevatten, is het mogelijk om elk van deze interfaceleden toe te wijzen aan één klasse of struct-lid.
Voorbeeld:
interface IControl { void Paint(); } interface IForm { void Paint(); } class Page : IControl, IForm { public void Paint() {...} }Hier worden de
Paintmethoden van beideIControlenIFormtoegewezen aan dePaintmethode inPage. Het is natuurlijk ook mogelijk om afzonderlijke expliciete interfacelid-implementaties te hebben voor de twee methoden.eindvoorbeeld
Als een klasse of struct een interface implementeert die verborgen leden bevat, moeten sommige leden mogelijk worden geïmplementeerd via expliciete implementaties van interfaceleden.
Voorbeeld:
interface IBase { int P { get; } } interface IDerived : IBase { new int P(); }Een implementatie van deze interface vereist ten minste één expliciete implementatie van het interfacelid en zou een van de volgende vormen aannemen
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() {...} }eindvoorbeeld
Wanneer een klasse meerdere interfaces implementeert die dezelfde basisinterface hebben, kan er slechts één implementatie van de basisinterface zijn.
Voorbeeld: In de volgende 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) {...} }het is niet mogelijk om afzonderlijke implementaties te hebben voor de
IControlbenoemde in de basisklasselijst, deIControlovergenomen doorITextBoxen deIControlovergenomen doorIListBox. Er bestaat namelijk geen idee van een afzonderlijke identiteit voor deze interfaces. De implementaties vanITextBoxenIListBoxdelen dezelfde implementatie vanIControl, enComboBoxwordt simpelweg beschouwd als het implementeren van drie interfaces:IControl,ITextBoxenIListBox.eindvoorbeeld
De leden van een basisklasse nemen deel aan interface mapping.
Voorbeeld: In de volgende code
interface Interface1 { void F(); } class Class1 { public void F() {} public void G() {} } class Class2 : Class1, Interface1 { public new void G() {} }De methode
FinClass1wordt gebruikt in de implementatie vanClass2's.eindvoorbeeld
19.6.6 Interface-implementatieovername
Een klasse neemt alle interface-implementaties over die worden geleverd door de basisklassen.
Zonder expliciet een interface opnieuw te implementeren, kan een afgeleide klasse de interfacetoewijzingen die worden overgenomen van de basisklassen niet wijzigen.
Voorbeeld: In de declaraties
interface IControl { void Paint(); } class Control : IControl { public void Paint() {...} } class TextBox : Control { public new void Paint() {...} }de
Paint-methode inTextBoxverbergt dePaint-methode inControl, maar de toewijzing vanControl.PaintaanIControl.Paintwordt niet gewijzigd, en oproepen naarPaintvia klasse-exemplaren en interface-exemplaren zullen de volgende effecten hebbenControl 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();eindvoorbeeld
Wanneer een interfacemethode echter is toegewezen aan een virtuele methode in een klasse, is het mogelijk dat afgeleide klassen de virtuele methode overschrijven en de implementatie van de interface wijzigen.
Voorbeeld: De bovenstaande declaraties herschrijven naar
interface IControl { void Paint(); } class Control : IControl { public virtual void Paint() {...} } class TextBox : Control { public override void Paint() {...} }de volgende effecten worden nu waargenomen
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();eindvoorbeeld
Omdat expliciete implementaties van interfaceleden niet virtueel kunnen worden gedeclareerd, is het niet mogelijk om een expliciete implementatie van interfaceleden te overschrijven. Het is echter perfect geldig voor een expliciete implementatie van interfaceleden om een andere methode aan te roepen en die andere methode kan worden gedeclareerd als virtueel, zodat afgeleide klassen deze kunnen overschrijven.
Voorbeeld:
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() { PaintControl(); } protected virtual void PaintControl() {...} } class TextBox : Control { protected override void PaintControl() {...} }Hier kunnen klassen die zijn afgeleid van
Control, de implementatie vanIControl.Paintspecialiseren door dePaintControl-methode te overschrijven.eindvoorbeeld
19.6.7 Interface re-implementation
Een klasse die een interface-implementatie over neemt, kan de interface opnieuw implementeren door deze op te nemen in de lijst met basisklassen.
Een her-implementatie van een interface volgt precies dezelfde interfacetoewijzingsregels als een eerste implementatie van een interface. De overgenomen interfacetoewijzing heeft dus geen enkel effect op de interfacetoewijzing die is vastgesteld voor de her-implementatie van de interface.
Voorbeeld: In de declaraties
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() {...} } class MyControl : Control, IControl { public void Paint() {} }het feit dat
ControlIControl.PaintopControl.IControl.Painttoewijst, heeft geen invloed op de heruitvoering inMyControl, dieIControl.PaintopMyControl.Painttoewijst.eindvoorbeeld
Geërfde openbare liddeclaraties en geërfde expliciete interfaceliddeclaraties nemen deel aan het interfacetoewijzingsproces voor re-geïmplementeerde interfaces.
Voorbeeld:
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 wordt de implementatie van
IMethodsinDerivedtoegewezen aan de interfacemethoden opDerived.F,Base.IMethods.G,Derived.IMethods.H, enBase.I.eindvoorbeeld
Wanneer een klasse een interface implementeert, implementeert deze impliciet ook alle basisinterfaces van die interface. Een her-implementatie van een interface is ook impliciet een her-implementatie van alle basisinterfaces van de interface.
Voorbeeld:
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 wordt de herimplementatie van
IDerivedook toegepast opIBase, waarbijIBase.Fwordt toegewezen aanD.F.eindvoorbeeld
19.6.8 Abstracte klassen en interfaces
Net als een niet-abstracte klasse biedt een abstracte klasse implementaties van alle abstracte leden van de interfaces die worden vermeld in de lijst met basisklassen van de klasse. Een abstracte klasse mag echter interfacemethoden toewijzen aan abstracte methoden.
Voorbeeld:
interface IMethods { void F(); void G(); } abstract class C : IMethods { public abstract void F(); public abstract void G(); }Hier kaart de implementatie van
IMethodsdeFenGnaar abstracte methoden, die worden overschreven in niet-abstracte klassen afgeleid vanC.eindvoorbeeld
Expliciete implementaties van interfaceleden kunnen niet abstract zijn, maar expliciete implementaties van interfaceleden zijn natuurlijk toegestaan abstracte methoden aan te roepen.
Voorbeeld:
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 moeten niet-abstracte klassen die zijn afgeleid van
C,FFenGGoverschrijven om de daadwerkelijke implementatie vanIMethodste bieden.eindvoorbeeld
ECMA C# draft specification