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.
7.1 Toepassing opstarten en beëindigen
7.1.1 Algemeen
Een programma kan worden gecompileerd als een klassebibliotheek die moet worden gebruikt als onderdeel van andere toepassingen of als een toepassing die rechtstreeks kan worden gestart. Het mechanisme voor het bepalen van deze compilatiemodus is door de implementatie gedefinieerd en extern voor deze specificatie.
Wanneer een toepassing wordt uitgevoerd, wordt er een nieuw toepassingsdomein gemaakt. Verschillende instantiëringen van een toepassing kunnen tegelijkertijd op dezelfde computer aanwezig zijn en elk een eigen toepassingsdomein heeft. Een toepassingsdomein maakt toepassingsisolatie mogelijk door te fungeren als een container voor de toepassingsstatus. Een toepassingsdomein fungeert als een container en grens voor de typen die in de toepassing zijn gedefinieerd en de klassebibliotheken die worden gebruikt. Typen die in één toepassingsdomein worden geladen, verschillen van dezelfde typen die in een ander toepassingsdomein worden geladen en exemplaren van objecten worden niet rechtstreeks gedeeld tussen toepassingsdomeinen. Elk toepassingsdomein heeft bijvoorbeeld een eigen kopie van statische variabelen voor deze typen en een statische constructor voor een type wordt maximaal één keer per toepassingsdomein uitgevoerd. Implementaties hebben de vrijheid om zelfgedefinieerd beleid en mechanismen te bieden voor het creëren en verwijderen van toepassingsdomeinen.
Een toepassingsuitvoering wordt gestart door een toegangspunt aan te roepen dat tijdens het compileren is geselecteerd vanaf kandidaat-toegangspunten die op een van de volgende manieren zijn opgegeven:
- Expliciet door een methode met de juiste kenmerken te declareren (§7.1.2).
- Impliciet, met behulp van instructies op het hoogste niveau (§7.1.3).
- Door de implementatie gedefinieerd, met behulp van een mechanisme buiten deze specificatie om het toegangspunt aan te wijzen (§7.1.4).
Opmerking: met uitzondering van kandidaten op het hoogste niveau (§7.1.3) zijn alle kandidaatinvoerpunten standaardmethoden; als kandidaat of het geselecteerde toegangspunt wordt niet voorkomen dat deze methoden als normaal worden aangeroepen. eindnotitie
Het is een compilatiefout als er geen kandidaat-toegangspunten zijn.
Anders wordt één kandidaat tijdens het compileren geselecteerd als het ingangspunt na het proces in §7.1.5.
Een toepassing wordt uitgevoerd door het geselecteerde toegangspunt aan te roepen, zoals beschreven in §7.1.6.
De beëindigingsstatuscode, die bedoeld is om communicatie van geslaagde of mislukte communicatie met de hostomgeving mogelijk te maken, wordt bepaald op basis van het resultaat van de aanroep van het toegangspunt (§7.1.6).
7.1.2 Benoemde toegangspunten
Een methode komt in aanmerking als een canditate ingangspunt door te voldoen aan de volgende vereisten:
- Het heeft de naam
Main. - Het zal zijn
static. - Het mag niet algemeen zijn.
- Het wordt gedeclareerd in een niet-algemeen type. Als het type dat de methode declareerde, een genest type is, kan geen van de bijbehorende insluittypen algemeen zijn.
- Het retourtype moet
void,int,System.Threading.Tasks.Task, ofSystem.Threading.Tasks.Task<int>. - Het kan de
asyncwijzigingsfunctie alleen hebben als het retourtype van de methode isSystem.Threading.Tasks.TaskofSystem.Threading.Tasks.Task<int>. - Het is geen optionele gedeeltelijke methode (§15.6.9) zonder een uitvoeringsverklaring.
- De parameterlijst moet leeg zijn of één waardeparameter van het type
string[]hebben.
De gedeclareerde toegankelijkheid (§7.4.2) van een methode wordt genegeerd om in aanmerking te komen als kandidaat-ingangspunt. Bij het opstarten van de toepassing wordt het geselecteerde toegangspunt aangeroepen, ongeacht de gedeclareerde toegankelijkheid. Eventuele gedeclareerde toegankelijkheid blijft echter van toepassing als het geselecteerde kandidaat-toegangspunt wordt aangeroepen na het opstarten van de toepassing.
Opmerking: De vereisten hier geven niet op dat
Maindit een klasselid moet zijn, het kan een struct-lid zijn. eindnotitie
7.1.3 Met behulp van instructies op het hoogste niveau
Eén compilatie-eenheid (§14.2 in een toepassing kan een statement_list bevatten; de instructies op het hoogste niveau genoemd. De betekenis van de instructies op het hoogste niveau is semantisch gelijk aan het declareren van het volgende in de globale naamruimte:
partial class Program
{
static «AsyncAndReturnType» «Main»(string[] args)
{
«statement_list»
}
}
De klasse Program is een gedeeltelijke declaratie van het type (§15.2.7) die wordt gecombineerd met eventuele andere gedeeltelijke klassedeclaraties voor Program binnen de toepassing.
Opmerking: Dit betekent dat een instructie op het hoogste niveau dezelfde toegangsrechten heeft als een instructie in de hoofdtekst van een statische methode die is gedefinieerd in de klasse
Programin de globale naamruimte. eindnotitie
De naam «Main» van de methode is een tijdelijke aanduiding voor een niet-opgegeven implementatienaam die rechtstreeks toegankelijk is voor de uitvoeringsomgeving en niet rechtstreeks vanuit de toepassing kan worden verwezen. Verder is er geen vereiste dat de gebruikte naam geldig is als een C#-methodenaam.
Opmerking: Een weergave van de gebruikte naam kan echter worden verkregen als een
stringtijdens het uitvoeren van een instructie op het hoogste niveau als deze een methode aanroept die gebruikmaakt van hetCallerMemberNamekenmerk (§23.5.6.4). eindnotitie
Dit «statement_list» is een tijdelijke aanduiding voor statement_list van de compilatie-eenheid.
De parameter args valt binnen het bereik binnen de instructies op het hoogste niveau en niet anders. Regels voor conflicterende/schaduwregels zijn van toepassing. Deze parameter ontvangt de toepassingsparameters (§7.1.6).
Asynchrone bewerkingen zijn toegestaan in instructies op het hoogste niveau tot de mate waarin ze zijn toegestaan in instructies binnen een benoemde asynchrone invoerpuntmethode.
De vervanging van de «AsyncAndReturnType» tijdelijke aanduiding in de methodehandtekening wordt als volgt bepaald op basis van de inhoud van de instructies op het hoogste niveau:
| Instructies op het hoogste niveau bevatten | Gegenereerde handtekening |
|---|---|
Nee await of return met waarde |
static void «Main»(string[] args) |
return met alleen waarde |
static int «Main»(string[] args) |
await alleen |
static async Task «Main»(string[] args) |
await en return met waarde |
static async Task<int> «Main»(string[] args) |
Deze gegenereerde handtekeningen voldoen aan de vereisten voor benoemde toegangspunten (§7.1.2) en is daarom «Main» een kandidaat-ingangspunt.
Opmerking: deze handtekeningen bevatten geen gedeclareerde toegankelijkheid (§7.4.2). Een implementatie kan echter gedeclareerde toegankelijkheid omvatten, omdat toegankelijkheid wordt genegeerd voor ingangspuntmethoden (§7.1.2), zou de methode nog steeds semantisch gelijkwaardig zijn.
7.1.4 Extern gedefinieerd toegangspunt
Een implementatie kan een mechanisme bieden dat buiten deze specificatie staat om een kandidaat-ingangspunt op te geven.
Als dit mechanisme een type binnen het programma identificeert, wordt het kandidaatinvoerpunt bepaald zoals opgegeven voor benoemde toegangspunten (§7.1.2).
Als dit mechanisme een methode binnen het programma identificeert, kan de methode een naam hebben, maar anders moet voldoen aan de vereisten die zijn opgegeven voor benoemde toegangspunten (§7.1.2).
Het externe mechanisme mag slechts één kandidaatinvoerpunt opgeven.
Een kandidaatinvoerpunt dat door een extern mechanisme is opgegeven, heeft voorrang bij de selectie van toegangspunten (§7.1.5).
7.1.5 Selectie van toegangspunt
Het is een compilatiefout als er geen kandidaat-toegangspunten zijn.
Anders wordt het ingangspunt uit de kandidaten geselecteerd:
- Als een van de kandidaten wordt opgegeven door een extern mechanisme (§7.1.4) wordt deze geselecteerd als ingangspunt;
- Anders wordt een van de kandidaten gedefinieerd door instructies op het hoogste niveau (§7.1.3) die als ingangspunt wordt geselecteerd;
- Anders:
- Als een van de kandidaten een retourtype of
intvoideen van de kandidaten heeft die een retourtypeSystem.Threading.Tasks.Taskhebben ofSystem.Threading.Tasks.Task<int>uit de kandidaatgroep worden verwijderd. - Als er één resterende kandidaat is, wordt deze geselecteerd als invoerpunt.
- Anders kan het ingangspunt niet worden bepaald en wordt er een compilatiefout gerapporteerd.
- Als een van de kandidaten een retourtype of
Het ingangspunt dat tijdens het compileren is geselecteerd, wordt tijdens runtime aangeroepen als onderdeel van het opstarten van de toepassing (§7.1.6).
7.1.6 Ingangspunt aanroepen
Als het invoerpunt een parameter declareert, geeft de implementatie als de initiële waarde van die parameter een niet-null-verwijzing naar een tekenreeksmatrix op. Deze matrix bestaat uit niet-null-verwijzingen naar nul of meer tekenreeksen, genaamd toepassingsparameters, die door de implementatie gedefinieerde waarden door de hostomgeving worden gegeven voordat de toepassing wordt opgestart.
Opmerking: Op systemen die een opdrachtregel ondersteunen, komen toepassingsparameters overeen met wat algemeen bekend staat als opdrachtregelargumenten. eindnotitie
Het opstart- en beëindigingsproces van de toepassing is semantisch gelijk aan de volgende stappen:
- Een toepassingsuitvoering wordt gestart door:
- Aanroepen (§12.8.10) de ingangsmethode, indien het retourtype is
voidof ; ofint - Wachtend op (§12.9.9) is het resultaat van het aanroepen van de invoerpuntmethode, als het retourtype een
Tasktype is. - Als voor het toegangspunt een argument is vereist, wordt de matrix van de toepassingsparameter opgegeven als waarde.
- Aanroepen (§12.8.10) de ingangsmethode, indien het retourtype is
Opmerking: als u de invoerpuntmethode aanroept, wordt de statische constructor, indien van toepassing, eerst uitgevoerd (§15.12, §16.8.10). eindnotitie
- De toepassing wordt beëindigd
- Als de uitvoering resulteert in een
intwaarde die fungeert als de beëindigingsstatuscode; - Als de uitvoering anders resulteert in geen retourwaarde, is
0de beëindigingsstatuscode ; - Als de uitvoering wordt beëindigd vanwege een uitzondering (§22.4), wordt de afsluitcode door de implementatie gedefinieerd. Daarnaast kan de implementatie alternatieve API's bieden voor het opgeven van de afsluitcode.
- Als de uitvoering resulteert in een
Of finalizers (§15.13) al dan niet worden uitgevoerd als onderdeel van de beëindiging van de toepassing, is door de implementatie gedefinieerd.
Note: De implementatie van het .NET Framework doet alle redelijke inspanningen om finalizers aan te roepen (§15.13) voor alle objecten die nog niet zijn verzameld, tenzij dergelijke opschoning is onderdrukt (bijvoorbeeld door een aanroep naar de bibliotheekmethode
GC.SuppressFinalize). eindnotitie
7.2 Verklaringen
Declaraties in een C#-programma definiëren de samenstellende elementen van het programma. C#-programma's zijn ingedeeld met behulp van naamruimten. Deze worden geïntroduceerd met behulp van naamruimtedeclaraties (§14), die typedeclaraties en geneste naamruimtedeclaraties kunnen bevatten. Typedeclaraties (§14.8) worden gebruikt voor het definiëren van klassen (§15), structs (§16), interfaces (§19), enums (§20) en gemachtigden (§21). De soorten leden die in een typedeclaratie zijn toegestaan, zijn afhankelijk van de vorm van de typedeclaratie. Bijvoorbeeld, klassedeclaraties kunnen declaraties bevatten voor constanten (§15.4), velden (§15.5), methoden (§15.6), eigenschappen (§15.7), gebeurtenissen (§15.8), indexeerfuncties (§15.9), operators (§15.10), instantieconstructors (§15.11), statische constructors (§15.12), finalizers (§15.13) en geneste typen (§15.3.9).
Een declaratie definieert een naam in de declaratieruimte waartoe de declaratie behoort. Het is een compilatiefout met twee of meer declaraties die leden met dezelfde naam in een declaratieruimte introduceren, behalve in de volgende gevallen:
- Twee of meer naamruimtedeclaraties met dezelfde naam zijn toegestaan in dezelfde declaratieruimte. Dergelijke naamruimtedeclaraties worden samengevoegd om één logische naamruimte te vormen en één declaratieruimte te delen.
- Declaraties in afzonderlijke programma's, maar in dezelfde ruimte voor naamruimtedeclaratie mogen dezelfde naam delen.
Opmerking: deze declaraties kunnen echter dubbelzinnigheden veroorzaken als ze in dezelfde toepassing zijn opgenomen. eindnotitie
- Twee of meer methoden met dezelfde naam, maar afzonderlijke handtekeningen zijn toegestaan in dezelfde declaratieruimte (§7.5).
- Twee of meer typedeclaraties met dezelfde naam, maar afzonderlijke getallen van typeparameters zijn toegestaan in dezelfde declaratieruimte (§7.7.2).
- Twee of meer typedeclaraties met de gedeeltelijke wijziging in dezelfde declaratieruimte kunnen dezelfde naam, hetzelfde aantal typeparameters en dezelfde classificatie (klasse, struct of interface) delen. In dit geval dragen de typedeclaraties bij aan één type en worden ze samengevoegd om één declaratieruimte te vormen (§15.2.7).
- Een naamruimtedeclaratie en een typedeclaratie in dezelfde declaratieruimte kunnen dezelfde naam delen zolang de typedeclaratie ten minste één typeparameter heeft (§7.7.2).
Er zijn verschillende typen declaratieruimten, zoals beschreven in het volgende.
- Binnen alle compilatie-eenheden van een programma zijn namespace_member_declarations die niet omsloten zijn door een namespace_declaration onderdeel van een enkele gecombineerde declaratieruimte die de globale declaratieruimte wordt genoemd.
- Binnen alle compilatie-eenheden van een programma zijn namespace_member_declarationbinnen namespace_declarations met dezelfde volledig gekwalificeerde naamruimtenaam lid van één gecombineerde declaratieruimte. Per §14.3 omvat dit file_scoped_namespace_declarations.
- Elke compilation_unit en namespace_body hebben een declaratieruimte voor aliassen. Elke extern_alias_directive en using_alias_directive van de compilation_unit of namespace_body draagt een lid bij aan de aliasdeclaratieruimte (§14.6.2).
- Elke niet-gedeeltelijke klasse-, struct- of interfacedeclaratie maakt een nieuwe declaratieruimte. Elke gedeeltelijke klasse-, struct- of interfacedeclaratie draagt bij aan een declaratieruimte die wordt gedeeld door alle overeenkomende onderdelen in hetzelfde programma (§16.2.4). Namen worden in deze declaratieruimte ingevoerd via class_member_declarations, struct_member_declarations, interface_member_declarations of type_parameters. Behalve voor overbelaste instantieconstructordeclaraties en statische constructordeclaraties kan een klasse, struct of interface geen liddeclaratie bevatten met dezelfde naam als de klasse, struct of interface. Een klasse, struct of interface staat de declaratie van overbelaste methoden en indexeerfuncties toe. Bovendien staat een klasse of struct de declaratie van overbelaste exemplaarconstructors en operators toe. Een klasse, struct of interface kan bijvoorbeeld meerdere methodedeclaraties met dezelfde naam bevatten, mits deze methodedeclaraties verschillen in hun handtekening (§7.5). Basisklassen dragen niet bij aan de declaratieruimte van een klasse en basisinterfaces dragen niet bij aan de declaratieruimte van een interface. Een afgeleide klasse of interface mag dus een lid met dezelfde naam declareren als een overgenomen lid. Een dergelijk lid wordt gezegd het overgenomen lid te verbergen .
- Elke gedelegeerdedeclaratie maakt een nieuwe declaratieruimte. Namen worden in deze declaratieruimte ingevoerd via parameters (fixed_parameteren parameter_collections) en type_parameters.
- Elke opsommingsdeclaratie maakt een nieuwe declaratieruimte. Namen worden in deze declaratieruimte ingevoerd via enum_member_declarations.
- Elke methodeverklaring, eigenschapverklaring, eigenschapstoegangsverklaring, indexeerderverklaring, indexeerdertoegangsverklaring, operatorverklaring, instantieconstructorverklaring, anonieme functieverklaring en lokale functieverklaring creëert een nieuwe declaratieruimte genaamd een lokale variabele declaratieruimte. Namen worden in deze declaratieruimte ingevoerd via parameters (fixed_parameteren parameter_collections) en type_parameters. De set en init-toegangsfunctie voor een eigenschap of een indexeerfunctie introduceren de naam
valueals parameter. De hoofdtekst van het functielid, de anonieme functie of de lokale functie, indien aanwezig, wordt beschouwd als genest binnen de declaratieruimte van de lokale variabele. Wanneer een ruimte voor declaratie van lokale variabelen en een geneste ruimte voor lokale variabelen elementen bevatten met dezelfde naam, binnen het bereik van de geneste lokale naam, wordt de buitenste lokale naam verborgen (§7.6.1) door de geneste lokale naam.Opmerking: Verwijder parameters van anonieme functies (§12.22.2) introduceer geen namen in een declaratieruimte. eindnotitie
- Er kunnen extra lokale declaratieruimten voor variabelen optreden binnen liddeclaraties, anonieme functies en lokale functies. Namen worden in deze declaratieruimten geïntroduceerd via patroons, declaration_expressions, declaration_statementen exception_specifiers. Lokale variabelendeclaratieruimten kunnen genest zijn, maar het is een fout als een lokale variabelendeclaratieruimte en een geneste lokale variabelendeclaratieruimte elementen met dezelfde naam bevatten. In een geneste declaratieruimte is het dus niet mogelijk om een lokale variabele, lokale functie of constante te declareren met dezelfde naam als een parameter, typeparameter, lokale variabele, lokale functie of constante in een ingesloten declaratieruimte. Het is mogelijk dat twee declaratieruimten elementen met dezelfde naam bevatten zolang geen van beide declaratieruimte de andere bevat. Lokale declaratieruimten worden gemaakt door de volgende constructies:
- Elke variable_initializer in een veld- en eigenschapsdeclaratie introduceert een eigen lokale ruimte voor variabeledeclaratie, die niet is genest binnen een andere ruimte voor declaratie van lokale variabelen.
- De hoofdtekst van een functielid, anonieme functie of lokale functie, indien van toepassing, maakt een lokale ruimte voor variabeledeclaratie die wordt beschouwd als genest binnen de declaratieruimte van de lokale variabele van de functie.
- Elke constructor_initializer maakt een lokale ruimte voor variabeledeclaratie die is genest binnen de declaratie van de instantieconstructor. De ruimte voor declaratie van lokale variabelen voor de hoofdtekst van de constructor is op zijn beurt genest binnen deze lokale ruimte voor declaratie van variabelen.
- Elk blok, switch_block, specific_catch_clause, iteration_statement en using_statement maakt een geneste ruimte voor lokale variabeledeclaratie.
- Elke embedded_statement die niet rechtstreeks deel uitmaakt van een statement_list maakt een geneste ruimte voor declaratie van lokale variabelen.
- Elke switch_section maakt een geneste lokale variabeledeclaratieruimte. Variabelen die rechtstreeks in de statement_list van de switch_section worden gedeclareerd (maar niet binnen een geneste ruimte voor lokale variabelen binnen de statement_list) worden echter rechtstreeks toegevoegd aan de ruimte voor lokale variabelendeclaratie van de switch_block in plaats van die van de switch_section.
- De syntactische vertaling van een query_expression (§12.23.3) kan een of meer lambda-expressies introduceren. Als anonieme functies maakt elk van deze functies een lokale ruimte voor variabeledeclaratie zoals hierboven beschreven.
- Elk blok of switch_block maakt een afzonderlijke declaratieruimte voor labels. Namen worden in deze declaratieruimte binnengebracht via labeled_statements en de namen worden verwezen via goto_statements. De labeldeclaratieruimte van een blok omvat ook ingesloten blokken. Daarom is het binnen een geneste blok niet mogelijk om een label met dezelfde naam als een label in een omsluitblok te declareren.
Opmerking: het feit dat variabelen die rechtstreeks in een switch_section zijn gedeclareerd, worden toegevoegd aan de lokale ruimte voor variabeledeclaratie van de switch_block in plaats van de switch_section kunnen leiden tot verrassende code. In het onderstaande voorbeeld bevindt de lokale variabele
yzich binnen het bereik van de switchsectie voor de standaardcase, ondanks de declaratie die wordt weergegeven in de switchsectie voor case 0. De lokale variabelezvalt niet binnen het bereik van de switchsectie voor de standaardcase, omdat deze wordt geïntroduceerd in de ruimte voor lokale variabeledeclaratie voor de switchsectie waarin de declaratie plaatsvindt.int x = 1; switch (x) { case 0: int y; break; case var z when z < 10: break; default: y = 10; // Valid: y is in scope Console.WriteLine(x + y); // Invalid: z is not scope Console.WriteLine(x + z); break; }eindnotitie
De tekstvolgorde waarin namen worden gedeclareerd, is over het algemeen van geen betekenis. Met name de tekstvolgorde is niet belangrijk voor de declaratie en het gebruik van naamruimten, constanten, methoden, eigenschappen, gebeurtenissen, indexeerfuncties, operators, instantieconstructors, finalizers, statische constructors en typen. De volgorde van declaraties is op de volgende manieren aanzienlijk:
- Declaratievolgorde voor velddeclaraties bepaalt de volgorde waarin hun initialisatieprogramma's (indien aanwezig) worden uitgevoerd (§15.5.6.2, §15.5.6.3).
- Lokale variabelen worden gedefinieerd voordat ze worden gebruikt (§7.6).
- De declaratievolgorde voor enumliddeclaraties (§20.4) is significant wanneer constant_expression waarden worden weggelaten.
Voorbeeld: De declaratieruimte van een naamruimte is 'open ended' en twee naamruimtedeclaraties met dezelfde volledig gekwalificeerde naam dragen bij aan dezelfde declaratieruimte. Bijvoorbeeld
namespace Megacorp.Data { class Customer { ... } } namespace Megacorp.Data { class Order { ... } }De twee bovenstaande naamruimtedeclaraties dragen bij aan dezelfde declaratieruimte, in dit geval het declareren van twee klassen met de volledig gekwalificeerde namen
Megacorp.Data.CustomerenMegacorp.Data.Order. Omdat de twee declaraties bijdragen aan dezelfde declaratieruimte, zou dit een compilatiefout hebben veroorzaakt als elk een declaratie van een klasse met dezelfde naam bevatte.eindvoorbeeld
Opmerking: Zoals hierboven is opgegeven, bevat de declaratieruimte van een blok de geneste blokken. In het volgende voorbeeld resulteren de
FenGmethoden dus in een compilatietijdfout omdat de naamiwordt gedeclareerd in het buitenste blok en niet opnieuw kan worden gedeclareerd in het binnenste blok. DeHenImethoden zijn echter geldig omdat de tweeiworden gedeclareerd in afzonderlijke niet-geneste blokken.class A { void F() { int i = 0; if (true) { int i = 1; } } void G() { if (true) { int i = 0; } int i = 1; } void H() { if (true) { int i = 0; } if (true) { int i = 1; } } void I() { for (int i = 0; i < 10; i++) { H(); } for (int i = 0; i < 10; i++) { H(); } } }eindnotitie
7.3 Leden
7.3.1 Algemeen
Naamruimten en typen hebben lid-s.
Opmerking: de leden van een entiteit zijn algemeen beschikbaar via het gebruik van een gekwalificeerde naam die begint met een verwijzing naar de entiteit, gevolgd door een token '
.' gevolgd door de naam van het lid. eindnotitie
Leden van een type worden gedeclareerd in de typedeclaratie of overgenomen van de basisklasse van het type. Wanneer een type wordt overgenomen van een basisklasse, worden alle leden van de basisklasse, behalve instantieconstructors, finalizers en statische constructors lid van het afgeleide type. De gedeclareerde toegankelijkheid van een basisklasselid bepaalt niet of het lid wordt overgenomen. Overname wordt uitgebreid naar een lid dat geen instantieconstructor, statische constructor of finalizer is.
Opmerking: een overgenomen lid is mogelijk niet toegankelijk in een afgeleid type, bijvoorbeeld vanwege de gedeclareerde toegankelijkheid (§7.4.2). eindnotitie
7.3.2 Naamruimteleden
Naamruimten en typen die geen naamruimte insluiten, zijn lid van de globale naamruimte. Dit komt rechtstreeks overeen met de namen die zijn gedeclareerd in de globale declaratieruimte.
Naamruimten en typen die in een naamruimte zijn gedeclareerd, zijn lid van die naamruimte. Dit komt rechtstreeks overeen met de namen die zijn gedeclareerd in de declaratieruimte van de naamruimte.
Naamruimten hebben geen toegangsbeperkingen. Het is niet mogelijk om persoonlijke, beveiligde of interne naamruimten te declareren en namen van naamruimten zijn altijd openbaar toegankelijk.
7.3.3 Struct-leden
De leden van een struct zijn de leden die zijn gedeclareerd in de struct en de leden die zijn overgenomen van de directe basisklasse System.ValueType van de struct en de indirecte basisklasse object.
De leden van een eenvoudig type komen rechtstreeks overeen met de leden van het struct-type, aangeduid met het eenvoudige type (§8.3.5).
7.3.4 Opsommingsleden
De leden van een opsomming zijn de constanten die zijn gedeclareerd in de opsomming en de leden die zijn overgenomen van de directe basisklasse System.Enum van de opsomming en de indirecte basisklassen System.ValueType en object.
7.3.5 Klasleden
De leden van een klasse zijn de leden die zijn gedeclareerd in de klasse en de leden die zijn overgenomen van de basisklasse (met uitzondering van klasse object die geen basisklasse heeft). De leden die zijn overgenomen van de basisklasse omvatten de constanten, velden, methoden, eigenschappen, gebeurtenissen, indexeerfuncties, operators en typen van de basisklasse, maar niet de instantieconstructors, finalizers en statische constructors van de basisklasse. Basisklasseleden worden overgenomen zonder rekening te houden met hun toegankelijkheid.
Een klassedeclaratie kan declaraties bevatten van constanten, velden, methoden, eigenschappen, gebeurtenissen, indexeerfuncties, operators, instantieconstructors, finalizers, statische constructors en typen.
De leden van object (§8.2.3) en string (§8.2.5) komen rechtstreeks overeen met de leden van de klassetypen die ze alias hebben.
7.3.6 Interfaceleden
De leden van een interface zijn de leden die zijn gedeclareerd in de interface en in alle basisinterfaces van de interface.
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
7.3.7 Matrixleden
De leden van een matrix zijn de leden die zijn overgenomen van klasse System.Array.
7.3.8 Gedelegeerden
Een gemachtigde neemt leden over van de klasse System.Delegate. Daarnaast bevat het een methode met de naam Invoke met hetzelfde retourtype en dezelfde lijst met parameters die zijn opgegeven in de declaratie (§21.2). Een aanroep van deze methode gedraagt zich identiek aan een gemachtigde aanroep (§21.6) op dezelfde gemachtigde instantie.
Een implementatie kan extra leden bieden, hetzij via overname of rechtstreeks in de gedelegeerde zelf.
7.4 Toegang tot leden
7.4.1 Algemeen
Declaraties van leden staan controle over toegang tot leden toe. De toegankelijkheid van een lid wordt vastgesteld door de gedeclareerde toegankelijkheid (§7.4.2) van het lid in combinatie met de toegankelijkheid van het onmiddellijk bevattende type, indien van toepassing.
Wanneer toegang tot een bepaald lid is toegestaan, wordt gezegd dat het lid toegankelijk is. Als de toegang tot een bepaald lid daarentegen niet is toegestaan, wordt gezegd dat het lid niet toegankelijk is. Toegang tot een lid is toegestaan wanneer de tekstlocatie waar de toegang plaatsvindt, is opgenomen in het toegankelijkheidsdomein (§7.4.3) van het lid.
7.4.2 Opgegeven toegankelijkheid
De gedeclareerde toegankelijkheid van een lid kan een van de volgende zijn:
- Openbaar, dat is geselecteerd door een
publicmodifier op te nemen in de liddeclaratie. De intuïtieve betekenis vanpublicis 'toegang niet beperkt'. - Beveiligd, geselecteerd door het opgeven van een
protectedmodifier in de liddeclaratie. De intuïtieve betekenis vanprotectedis "toegang beperkt tot de met klasse of interface, of klassen of interfaces die zijn afgeleid van het betreffende type". - Intern, die wordt geselecteerd door een
internalmodifier op te nemen in de lidverklaring. De intuïtieve betekenis vaninternalis "toegang beperkt tot deze assemblage". - Beveiligd intern, dat wordt gekozen door zowel een
protectedals eeninternalmodifier op te nemen in de liddeclaratie. De intuïtieve betekenis vanprotected internalis 'toegankelijk binnen deze assembly, evenals typen die zijn afgeleid van de klasse die de inhoud bevat'. - Privé beschermd, dat wordt geselecteerd door zowel een
privateals eenprotectedmodifier in de liddefinitie op te geven. De intuïtieve betekenis vanprivate protectedis 'toegankelijk binnen deze assembly door de bevatde klasse en typen die zijn afgeleid van de inhoudsklasse'. - Privé, die wordt geselecteerd door een
privatemodificator op te nemen in de liddeclaratie. De intuïtieve betekenis vanprivateis 'toegang beperkt tot het betreffende type'.
Afhankelijk van de context waarin een liddeclaratie plaatsvindt, zijn alleen bepaalde typen gedeclareerde toegankelijkheid toegestaan. Wanneer een liddeclaratie geen wijzigingsfuncties voor toegang bevat, bepaalt de context waarin de declaratie plaatsvindt, bovendien de standaard gedeclareerde toegankelijkheid.
- Naamruimten hebben
publicimpliciet toegankelijkheid gedeclareerd. Er zijn geen toegangsaanpassingen toegestaan voor naamruimtedeclaraties. - Typen die rechtstreeks zijn gedeclareerd in compilatie-eenheden of naamruimten (in tegenstelling tot binnen andere typen), kunnen met
publicofinternalgedeclareerde toegankelijkheid hebben en hebben standaard toegankelijkheid vaninternal. - Klasleden kunnen elke van de toegestane soorten gedeclareerde toegankelijkheid hebben en standaard gesteld worden op
privategedeclareerde toegankelijkheid.Opmerking: een type dat is gedeclareerd als lid van een klasse, kan een van de toegestane soorten toegankelijkheid hebben, terwijl een type dat is gedeclareerd als lid van een naamruimte alleen
publicofinternaltoegankelijkheid kan hebben gedeclareerd. eindnotitie - Struct-leden kunnen
public,internalofprivategedeclareerde toegankelijkheid hebben en standaard hebben zijprivategedeclareerde toegankelijkheid, omdat structs impliciet verzegeld zijn. Struct-leden die zijn geïntroduceerd in eenstruct(dat wil zeggen, niet overgenomen door die struct) kunnen geenprotected,protected internal, ofprivate protectedgedeclareerde toegankelijkheid hebben.Opmerking: een type dat als lid van een struct is gedeclareerd, kan toegankelijkheid hebben zoals
public,internalofprivategedeclareerd, terwijl een type dat is gedeclareerd als lid van een naamruimte alleen toegankelijkheid kan hebben zoalspublicofinternal. eindnotitie - Interfaceleden hebben
publicimpliciet toegankelijkheid gedeclareerd. - Opsommingsleden hebben
publicimpliciet toegankelijkheid gedeclareerd. Er zijn geen toegangsmodifiers toegestaan voor inventarisatieliddeclaraties.
7.4.3 Toegankelijkheidsdomeinen
Het toegankelijkheidsdomein van een lid bestaat uit de (mogelijk niet-aaneengesloten) secties van programmatekst waarin toegang tot het lid is toegestaan. Voor het definiëren van het toegankelijkheidsdomein van een lid zegt men dat een lid toplevel is als het niet binnen een type wordt gedeclareerd, en een lid wordt genest als het binnen een ander type wordt gedeclareerd. Bovendien wordt de programmatekst van een programma gedefinieerd als alle tekst in alle compilatie-eenheden van het programma en wordt de programmatekst van een type gedefinieerd als alle tekst in de type_declarations van dat type (inclusief mogelijk typen die zijn genest binnen het type).
Het toegankelijkheidsdomein van een vooraf gedefinieerd type (zoals object, intof double) is onbeperkt.
Het toegankelijkheidsdomein van een niet-afhankelijk type T op het hoogste niveau (§8.4.4) dat in een programma P wordt gedeclareerd, wordt als volgt gedefinieerd:
- Als de gedeclareerde toegankelijkheid
Topenbaar is, is het toegankelijkheidsdomeinTde programmatekst vanPen elk programma waarnaar wordt verwezenP. - Als de gedeclareerde toegankelijkheid
Tintern is, is het toegankelijkheidsdomeinTde programmatekst vanP.
Opmerking: Uit deze definities volgt dat het toegankelijkheidsdomein van een niet-afhankelijk type op het hoogste niveau altijd ten minste de programmatekst is van het programma waarin dat type wordt gedeclareerd. eindnotitie
Het toegankelijkheidsdomein voor een samengesteld type T<A₁, ..., Aₑ> is het snijpunt van het toegankelijkheidsdomein van het niet-afhankelijke algemene type T en de toegankelijkheidsdomeinen van de typeargumenten A₁, ..., Aₑ.
Het toegankelijkheidsdomein van een geneste lid M dat is gedeclareerd in een type T binnen een programma P, wordt als volgt gedefinieerd (waarbij wordt aangegeven dat M zichzelf mogelijk een type is):
- Als de gedeclareerde toegankelijkheid van
Mpublicis, is het toegankelijkheidsdomein vanMhetzelfde als dat vanT. - Als de toegankelijkheid van
Mprotected internalis, moetDde samenvoeging zijn van de programmatekst vanPen de programmatekst van elk type dat vanTis afgeleid en buitenPis gedeclareerd. Het toegankelijkheidsdomein vanMis het snijpunt van het toegankelijkheidsdomein vanTmetD. - Als de gedeclareerde toegankelijkheid
Misprivate protected, laten weDhet snijpunt zijn van de programmatekst vanPen de programmatekst vanTen elk type dat is afgeleid vanT. Het toegankelijkheidsdomein vanMis het snijpunt van het toegankelijkheidsdomein vanTmetD. - Als de gedeclareerde toegankelijkheid
Misprotected, laat uDde samenvoeging zijn van de programmatekst vanTen de programmatekst van elk type dat is afgeleid vanT. Het toegankelijkheidsdomein vanMis het snijpunt van het toegankelijkheidsdomein vanTmetD. - Als de gedeclareerde toegankelijkheid
Misinternal, is het toegankelijkheidsdomeinMhet snijpunt van het toegankelijkheidsdomein vanTmet de programmatekst vanP. - Als de gedeclareerde toegankelijkheid
Misprivate, is het toegankelijkheidsdomeinMde programmatekst vanT.
Opmerking: Uit deze definities volgt het dat het toegankelijkheidsdomein van een genest lid altijd ten minste de programmatekst is van het type waarin het lid wordt gedeclareerd. Bovendien is het toegankelijkheidsdomein van een lid nooit inclusiever dan het toegankelijkheidsdomein van het type waarin het lid wordt gedeclareerd. eindnotitie
Opmerking: Wanneer een type of lid
Mwordt geopend, worden de volgende stappen geëvalueerd om ervoor te zorgen dat de toegang is toegestaan:
MAls deze wordt gedeclareerd binnen een type (in plaats van een compilatie-eenheid of een naamruimte), treedt er eerst een compilatiefout op als dat type niet toegankelijk is.- Vervolgens, als
Mpublicis, is toegang toegestaan.- Als dat niet het geval is
protected internal,Mis de toegang toegestaan als deze plaatsvindt in het programma waarinMwordt gedeclareerd, of als deze plaatsvindt binnen een klasse die is afgeleid van de klasse waarinMwordt gedeclareerd en plaatsvindt via het afgeleide klassetype (§7.4.4).MAls dat niet het geval isprotected, is de toegang toegestaan als deze plaatsvindt binnen de klasse waarinMwordt gedeclareerd, of als deze plaatsvindt binnen een klasse die is afgeleid van de klasse waarinMwordt gedeclareerd en plaatsvindt via het afgeleide klassetype (§7.4.4).- Anders, als
Minternalis, is de toegang toegestaan als het plaatsvindt binnen het programma waarinMwordt gedeclareerd.- Anders, als
Mprivateis, is de toegang toegestaan als dit gebeurt binnen het type waarinMwordt gedeclareerd.- Anders is het type of lid niet toegankelijk en treedt er een compileertijdfout op. eindnotitie
Voorbeeld: In de volgende code
public class A { public static int X; internal static int Y; private static int Z; } internal class B { public static int X; internal static int Y; private static int Z; public class C { public static int X; internal static int Y; private static int Z; } private class D { public static int X; internal static int Y; private static int Z; } }de klassen en leden beschikken over de volgende toegankelijkheidsdomeinen:
- Het toegankelijkheidsdomein van
AenA.Xis onbeperkt.- Het toegankelijkheidsdomein van
A.Y,B,B.X,B.Y,B.C,B.C.XenB.C.Yis de programmatekst van het programma.- Het toegankelijkheidsdomein van
A.Zis de programmatekst vanA.- Het toegankelijkheidsdomein van
B.ZenB.Dis de programmatekst vanB, inclusief de programmatekst vanB.CenB.D.- Het toegankelijkheidsdomein van
B.C.Zis de programmatekst vanB.C.- Het toegankelijkheidsdomein van
B.D.XenB.D.Yis de programmatekst vanB, inclusief de programmatekst vanB.CenB.D.- Het toegankelijkheidsdomein van
B.D.Zis de programmatekst vanB.D. Zoals in het voorbeeld wordt geïllustreerd, is het toegankelijkheidsdomein van een lid nooit groter dan die van een type dat het bevat. Hoewel alle leden bijvoorbeeld openbare toegankelijkheid hebben opgegeven, hebben alle behalveXtoegankelijkheidsdomeinen die beperkt worden door een omsluitend type.eindvoorbeeld
Zoals beschreven in §7.3 worden alle leden van een basisklasse, met uitzondering van instantieconstructors, finalizers en statische constructors, overgenomen door afgeleide typen. Dit omvat zelfs privéleden van een basisklasse. Het toegankelijkheidsdomein van een privélid bevat echter alleen de programmatekst van het type waarin het lid wordt gedeclareerd.
Voorbeeld: In de volgende code
class A { int x; static void F(B b) { b.x = 1; // Ok } } class B : A { static void F(B b) { b.x = 1; // Error, x not accessible } }de
Bklasse neemt het privélidxover van deAklasse. Omdat het lid privé is, is het alleen toegankelijk binnen de class_body vanA. In deb.xmethode slaagt de toegang totA.Fdus, maar in deB.Fmethode mislukt het.eindvoorbeeld
7.4.4 Beveiligde toegang
Wanneer een protected of private protected exemplaarlid wordt geopend buiten de programmatekst van de klasse waarin het wordt gedeclareerd en wanneer een protected internal exemplaarlid wordt geopend buiten de programmatekst van het programma waarin het wordt gedeclareerd, vindt de toegang plaats binnen een klassedeclaratie die is afgeleid van de klasse waarin het wordt gedeclareerd. Bovendien moet de toegang plaatsvinden via een exemplaar van dat afgeleide klassetype of een klassetype dat daaruit is samengesteld. Deze beperking voorkomt dat een afgeleide klasse toegang heeft tot beveiligde leden van andere afgeleide klassen, zelfs wanneer de leden worden overgenomen van dezelfde basisklasse. Leden van de instantieinterface die zijn gedefinieerd met protected of private protected toegang, kunnen niet worden geopend vanuit een class interface of struct die die interface implementeert. Deze kunnen alleen worden geopend vanuit afgeleide interfaces.
class Typen struct kunnen echter instantieleden implementeren protected die zijn gedeclareerd in een interface die ze implementeren.
Laten we B een basisklasse zijn die een beveiligd exemplaarlid Mdeclareert en een klasse is D die is afgeleid van B. Binnen de class_body (of record_class_body) van Dkan toegang tot M een van de volgende vormen worden gebruikt:
- Een niet-gekwalificeerde type_name of primary_expression in de vorm van
M. - Een primary_expression van de vorm
E.M, op voorwaarde dat het typeETis of een klasse die is afgeleid vanT, waarTde klasseDis, of een klassetype dat is samengesteld uitD. - Een primary_expression van de vorm
base.M. - Een primaire_expressie van de vorm
base[argumentenlijst].
Naast deze vormen van toegang heeft een afgeleide klasse toegang tot een beveiligde exemplaarconstructor van een basisklasse in een constructor_initializer (§15.11.2).
Voorbeeld: In de volgende code
public class A { protected int x; static void F(A a, B b) { a.x = 1; // Ok b.x = 1; // Ok } } public class B : A { static void F(A a, B b) { a.x = 1; // Error, must access through instance of B b.x = 1; // Ok } }binnen
A, is het mogelijk om toegangxte krijgen via exemplaren van beideAenB, aangezien in beide gevallen de toegang plaatsvindt via een exemplaar vanAof een klasse afgeleid vanA. BinnenBis het echter niet mogelijk om via een exemplaar vanxtoegang te krijgen totA, omdatAniet afkomstig is vanB.eindvoorbeeld
Voorbeeld:
class C<T> { protected T x; } class D<T> : C<T> { static void F() { D<T> dt = new D<T>(); D<int> di = new D<int>(); D<string> ds = new D<string>(); dt.x = default(T); di.x = 123; ds.x = "test"; } }Hier zijn de drie toewijzingen aan
xtoegestaan omdat ze allemaal plaatsvinden door middel van instanties van klassetypen die zijn samengesteld vanuit het generieke type.eindvoorbeeld
Opmerking: Het toegankelijkheidsdomein (§7.4.3) van een beveiligd lid dat in een algemene klasse is gedeclareerd, bevat de programmatekst van alle klassedeclaraties die zijn afgeleid van elk type dat is samengesteld uit die algemene klasse. In het voorbeeld:
class C<T> { protected static T x; } class D : C<string> { static void Main() { C<int>.x = 5; } }de verwijzing naar
protectedlidC<int>.xinDis geldig, ook al is de klasseDafgeleid vanC<string>. eindnotitie
7.4.5 Toegankelijkheidsbeperkingen
Voor verschillende constructies in de C#-taal moet een type ten minste zo toegankelijk zijn als lid of een ander type. Een type T wordt geacht minstens zo toegankelijk te zijn als lid of type M als het toegankelijkheidsdomein T een superset is van het toegankelijkheidsdomein van M. Met andere woorden, T is minstens zo toegankelijk als M als T toegankelijk is in alle contexten waarin M toegankelijk is.
De volgende toegankelijkheidsbeperkingen bestaan:
- De directe basisklasse van een klassetype moet ten minste zo toegankelijk zijn als het klassetype zelf.
- De expliciete basisinterfaces van een interfacetype moeten ten minste zo toegankelijk zijn als het interfacetype zelf.
- Het retourtype en de parametertypen van een delegate moeten ten minste zo toegankelijk zijn als het delegatetype zelf.
- Het type constante moet minstens zo toegankelijk zijn als de constante zelf.
- Het type veld moet minstens zo toegankelijk zijn als het veld zelf.
- Het retourtype en de parametertypen van een methode moeten ten minste zo toegankelijk zijn als de methode zelf.
- Het type van een eigenschap moet minstens zo toegankelijk zijn als de eigenschap zelf.
- Het type gebeurtenis moet minstens zo toegankelijk zijn als de gebeurtenis zelf.
- Het type en de parametertypen van een indexeerfunctie moeten ten minste zo toegankelijk zijn als de indexeerfunctie zelf.
- Het retourtype en de parametertypen van een operator moeten ten minste zo toegankelijk zijn als de operator zelf.
- De parametertypen van een exemplaarconstructor moeten ten minste zo toegankelijk zijn als de instantieconstructor zelf.
- Een interface- of klassetypebeperking voor een typeparameter moet ten minste zo toegankelijk zijn als het lid dat de beperking declareert.
Voorbeeld: In de volgende code
class A {...} public class B: A {...}de
Bklasse veroorzaakt een compilatiefout omdatAniet minstens zo toegankelijk is alsB.eindvoorbeeld
Voorbeeld: Op dezelfde manier, in de volgende code
class A {...} public class B { A F() {...} internal A G() {...} public A H() {...} }de
Hmethode resulteert inBeen compilatiefout omdat het retourtypeAniet ten minste zo toegankelijk is als de methode.eindvoorbeeld
7.5 Handtekeningen en overbelasting
Methoden, instantieconstructors, indexeerfuncties en operators worden gekenmerkt door hun handtekening:
- De handtekening van een methode bestaat uit de naam van de methode, het aantal parameters van het type en het type en de parameterdoorgiftemodus van elk van de parameters, beschouwd in de volgorde van links naar rechts. Voor deze doeleinden wordt elk type parameter van de methode die voorkomt in het type van een parameter niet geïdentificeerd door de naam, maar door de rangschikkelijkheid in de parameterlijst van het type van de methode. De handtekening van een methode bevat niet specifiek het retourtype, parameternamen, type parameternamen, parameterbeperkingen, de
params,scopedofthisparameter modifiers, noch of parameters vereist of optioneel zijn. - De handtekening van een exemplaarconstructor bestaat uit het type en de parameterdoorgiftemodus van elk van de parameters, in de volgorde van links naar rechts. De handtekening van een instantieconstructor bevat niet specifiek de
paramswijzigingsfunctie die kan worden opgegeven voor de meest rechtse parameter, noch of parameters vereist of optioneel zijn. - De handtekening van een indexeerfunctie bestaat uit het type van elk van de parameters, in de volgorde van links naar rechts. De handtekening van een indexeerfunctie bevat niet specifiek het elementtype of de
scopedwijzigingsfunctie, noch bevat deze deparamswijzigingsfunctie die kan worden opgegeven voor de meest rechtse parameter, of descopedwijzigingsfunctie, noch of parameters vereist of optioneel zijn. - De handtekening van een operator bestaat uit de naam van de operator en het type van elk van de parameters, beschouwd in de volgorde van links naar rechts. De handtekening van een operator bevat niet specifiek het resultaattype of de
scopedwijzigingsfunctie. - De handtekening van een conversieoperator bestaat uit het brontype en het doeltype. De impliciete of expliciete classificatie van een conversieoperator maakt geen deel uit van de handtekening en is evenmin de
scopedwijzigingsfunctie. - Twee handtekeningen van hetzelfde type lid (methode, instantieconstructor, indexeerfunctie of operator) worden beschouwd als dezelfde handtekeningen als ze dezelfde naam hebben, het aantal typeparameters, het aantal parameters en de modi voor het doorgeven van parameters en een identiteitsconversie bestaat tussen de typen van de bijbehorende parameters (§10.2.2).
Handtekeningen zijn het inschakelende mechanisme voor het overbelasten van leden in klassen, structs en interfaces:
- Door overbelasting van methoden kan een klasse, struct of interface meerdere methoden met dezelfde naam declareren, mits hun handtekeningen uniek zijn binnen die klasse, struct of interface.
- Door het overladen van instantieconstructoren kan een klasse of struct meerdere instantieconstructoren declareren, mits hun signaturen uniek zijn binnen die klasse of struct.
- Door overbelasting van indexeerfuncties kan een klasse, struct of interface meerdere indexeerfuncties declareren, mits hun handtekeningen uniek zijn binnen die klasse, struct of interface.
- Door overbelasting van operators kan een klasse of struct meerdere operators met dezelfde naam declareren, mits hun handtekeningen uniek zijn binnen die klasse of struct.
Hoewel parameter_mode_modifierals onderdeel van een handtekening worden beschouwd, kunnen leden die in één type zijn gedeclareerd, niet alleen verschillen in handtekening door deze wijzigingsaanpassingen. Er treedt een compilatiefout op als twee leden in hetzelfde type zijn gedeclareerd met signaturen die hetzelfde zouden zijn als alle parameters in beide methoden met out of in modifiers worden gewijzigd naar ref modifiers. Voor andere doeleinden van handtekeningkoppeling (bijvoorbeeld verbergen of overschrijven), worden parameter_mode_modifiers beschouwd als onderdeel van de handtekening en komen ze niet overeen met elkaar.
Opmerking: Met deze beperking kunnen C#-programma's eenvoudig worden vertaald om te worden uitgevoerd op een platform dat geen manier biedt om methoden te definiëren die alleen verschillen in hun parameter_mode_modifiers. eindnotitie
De typen object en dynamic worden niet onderscheiden bij het vergelijken van handtekeningen. Leden die zijn gedeclareerd in één type waarvan de handtekeningen alleen verschillen door object te vervangen door dynamic zijn niet toegestaan.
Voorbeeld: In het volgende voorbeeld ziet u een set overbelaste methodedeclaraties samen met hun handtekeningen.
interface ITest { void F(); // F() void F(int x); // F(int) void F(ref int x); // F(ref int) void F(ref readonly int x); // F(ref int) error void F(out int x); // F(out int) error void F(object o); // F(object) void F(dynamic d); // error. void F(int x, int y); // F(int, int) int F(string s); // F(string) int F(int x); // F(int) error void F(string[] a); // F(string[]) void F(params string[] a); // F(string[]) error void F<S>(S s); // F<0>(0) void F<T>(T t); // F<0>(0) error void F<S,T>(S s); // F<0,1>(0) void F<T,S>(S s); // F<0,1>(1) ok }Houd er rekening mee dat een parameter_mode_modifier(§15.6.2) deel uitmaakt van een handtekening.
F(int)F(ref int)F(in int)F(out int)F(ref readonly int)Dit zijn dus allemaal unieke handtekeningen. MaarF(in int)F(out int), enF(ref int)F(ref readonly int)kunnen niet binnen dezelfde interface worden gedeclareerd omdat hun handtekeningen alleen verschillen door hun parameter_mode_modifiers. Houd er ook rekening mee dat het retourtype en deparamswijzigingsfunctie geen deel uitmaken van een handtekening, dus het is niet mogelijk om alleen te overbelasten op basis van het retourtype of op de opname of uitsluiting van deparamswijzigingsfunctie. Als zodanig resulteren de declaraties van de methodenF(int)enF(params string[])die hierboven zijn geïdentificeerd, tot een compilatiefout. eindvoorbeeld
7.6 Bereiken
7.6.1 Algemeen
Het bereik van een naam is de regio van programmatekst waarin het mogelijk is om te verwijzen naar de entiteit die is gedeclareerd door de naam zonder kwalificatie van de naam. Bereiken kunnen worden genest en een binnenste bereik kan de betekenis van een naam van een buitenste bereik opnieuw declareren. (Dit verwijdert echter niet de beperking die is opgelegd door §7.2 dat het binnen een geneste blok niet mogelijk is om een lokale variabele of lokale constante met dezelfde naam als een lokale variabele of lokale constante in een omsluitblok te declareren.) De naam van het buitenste bereik wordt vervolgens verborgen in de regio van de programmatekst die wordt gedekt door het binnenste bereik en toegang tot de buitenste naam is alleen mogelijk door de naam in aanmerking te komen.
Opmerking: het is mogelijk niet mogelijk om toegang te krijgen tot de verborgen buitennaam van het binnenste bereik omdat er geen manier is om deze in aanmerking te komen, zoals met typeparameters voor geneste typedeclaraties. eindnotitie
- Het bereik van een naamruimtelid dat is gedeclareerd door een namespace_member_declaration (§14.7) zonder namespace_declaration is de volledige programmatekst.
- Het bereik van een naamruimtelid dat is gedeclareerd door een compilation_unit_body waarvan de volledig gekwalificeerde naam is
N, is elke compilation_unit_body waarvan de volledig gekwalificeerde naam isNof begint metN, gevolgd door een punt. - Het bereik van een naamruimtelid dat is gedeclareerd door een namespace_member_declaration binnen een namespace_declaration waarvan de volledig gekwalificeerde naam is
N, is de namespace_body van elke namespace_declaration waarvan de volledig gekwalificeerde naam isNof begint metN, gevolgd door een punt. Per §14,3 worden file_scoped_namespace_declarations behandeld als gelijkwaardige namespace_declarations voor deze regel. - Het bereik van een naam die is gedefinieerd door een extern_alias_directive (§14.4) strekt zich uit over de global_using_directives, using_directives, global_attributes en compilation_unit_body van de naam die onmiddellijk compilation_unit of namespace_body bevat. Een extern_alias_directive draagt geen nieuwe leden bij aan de onderliggende declaratieruimte. Met andere woorden, een extern_alias_directive is niet transitief, maar is in plaats daarvan alleen van invloed op de compilation_unit, namespace_body en file_scoped_namespace_declaration waarin deze plaatsvindt.
- Het bereik van een naam die is gedefinieerd of geïmporteerd door een global_using_directive breidt zich uit over de global_attributes en compilation_unit_body van alle compilation_units in het programma.
- Het bereik van een naam die is gedefinieerd of geïmporteerd door een using_directive (§14.6) strekt zich uit over de global_attributes, statement_lists en namespace_member_declarationvan de compilation_unit of namespace_body waarin de using_directive plaatsvindt. Een using_directive kan nul of meer naamruimte- of typenamen beschikbaar maken binnen een bepaalde compilation_unit of namespace_body, maar draagt geen nieuwe leden bij aan de onderliggende declaratieruimte. Met andere woorden, een using_directive is niet transitief, maar is alleen van invloed op de compilation_unit of namespace_body waarin deze plaatsvindt.
- Het bereik van een typeparameter die is gedeclareerd door een type_parameter_list op een class_declaration (§15.2) is de class_base, type_parameter_constraints_clauseen class_body (of record_class_body) van die class_declaration.
Opmerking: In tegenstelling tot leden van een klasse, wordt dit bereik niet uitgebreid naar afgeleide klassen. eindnotitie
- Het bereik van een typeparameter die is gedeclareerd door een type_parameter_list op een struct_declaration (§16.2) is de struct_interfaces, type_parameter_constraints_clauses en struct_body (of record_struct_body) van die struct_declaration.
- Het bereik van een typeparameter die is gedeclareerd door een type_parameter_list op een interface_declaration (§19.2) is de interface_base, type_parameter_constraints_clauses en interface_body van die interface_declaration.
- Het bereik van een typeparameter die is gedeclareerd door een type_parameter_list op een delegate_declaration (§21.2) is de return_type, parameter_list en type_parameter_constraints_clausevan die delegate_declaration.
- Het bereik van een typeparameter die is gedeclareerd door een type_parameter_list op een method_declaration (§15.6.1) is de method_declaration en
nameofexpressies in een kenmerk op de method_declaration of de bijbehorende parameters. - Het bereik van een lid dat is gedeclareerd door een class_member_declaration (§15.3.1) is de class_body (of record_class_body) waarin de aangifte plaatsvindt. Daarnaast is het bereik van een klasselid uitgebreid tot de class_body (of record_class_body) van die afgeleide klassen die zijn opgenomen in het toegankelijkheidsdomein (§7.4.3) van het lid.
- Het bereik van een lid dat door een struct_member_declaration (§16.3) is gedeclareerd, is de struct_body (of record_struct_body) waarin de aangifte plaatsvindt.
- Het bereik van een lid dat is gedeclareerd door een enum_member_declaration (§20.4) is de enum_body waarin de verklaring plaatsvindt.
- Het bereik van een parameter die is gedeclareerd in een method_declaration (§15.6) is de method_body of ref_method_body van die method_declaration en
nameofexpressies in een kenmerk voor de methodedeclaratie of de bijbehorende parameters. - Het bereik van een parameter die is gedeclareerd in een indexer_declaration (§15.9) is de indexer_body van die indexer_declaration.
- Het bereik van een parameter die is gedeclareerd in een operator_declaration (§15.10) is de operator_body van die operator_declaration.
- Het bereik van een parameter die is gedeclareerd in een constructeurverklaring (§15.11) is de constructeurinitialisatie en het blok van die constructeurverklaring.
- Met uitzondering van verwijderingsparameters (§12.22.2), is het bereik van een parameter die is gedeclareerd in een lambda_expression (§12.22) de lambda_expression_body van die lambda_expression en
nameofexpressies in een kenmerk op de anonieme functie of de bijbehorende parameters. - Met uitzondering van verwijderingsparameters (§12.22.2), is het bereik van een parameter die is gedeclareerd in een anonymous_method_expression (§12.22) het blok van dat anonymous_method_expression en
nameofexpressies in een kenmerk op de anonieme functie of de bijbehorende parameters. - Het bereik van een label dat is gedeclareerd in een labeled_statement (§13.5) is het blok waarin de declaratie plaatsvindt.
- Het bereik van een lokale variabele die is gedeclareerd in een local_variable_declaration (§13.6.2) is het blok waarin de declaratie plaatsvindt.
- Het bereik van een lokale variabele die is gedeclareerd in een switch_block van een
switchinstructie (§13.8.3) is de switch_block. - Het bereik van een lokale variabele die is gedeclareerd in een for_initializer van een
forinstructie (§13.9.4) omvat de for_initializer, for_condition, for_iterator, en embedded_statement van deforinstructie. - Het bereik van een lokale constante die is gedeclareerd in een local_constant_declaration (§13.6.3) is het blok waarin de declaratie plaatsvindt. Het is een compilatietijdfout om te verwijzen naar een lokale constante in een tekstuele positie die zich vóór zijn constant_declarator bevindt.
- Het bereik van een variabele die is gedeclareerd als onderdeel van een foreach_statement, using_statement, lock_statement of query_expression wordt bepaald door de uitbreiding van de opgegeven constructie.
Binnen het bereik van een naamruimte, klasse, struct of opsommingslid is het mogelijk om te verwijzen naar het lid in een tekstuele positie die voorafgaat aan de declaratie van het lid.
Voorbeeld:
class A { void F() { i = 1; } int i = 0; }Hier is het geldig om
Fnaarite verwijzen voordat deze wordt gedeclareerd.eindvoorbeeld
Binnen het bereik van een lokale variabele is het een compilatiefout om naar de lokale variabele te verwijzen in een tekstuele positie die voorafgaat aan zijn declarator.
Voorbeeld:
class A { int i = 0; void F() { i = 1; // Error, use precedes declaration int i; i = 2; } void G() { int j = (j = 1); // Valid } void H() { int a = 1, b = ++a; // Valid } }In de bovenstaande methode verwijst de eerste toewijzing naar
Fspecifiek niet naar het veld dat in het buitenste bereik is gedeclareerd. In plaats daarvan verwijst deze naar de lokale variabele en resulteert deze in een compileertijdfout, omdat deze tekstually voorafgaat aan de declaratie van de variabele. In deG-methode is het gebruik vanjin de initialisatiefunctie voor de declaratie vanjgeldig, omdat het gebruik niet voorafgaat aan de declarator. In deHmethode verwijst een volgende declaratie juist naar een lokale variabele die is gedeclareerd in een eerdere declaratie binnen dezelfde local_variable_declaration.eindvoorbeeld
Opmerking: De bereikregels voor lokale variabelen en lokale constanten zijn ontworpen om te garanderen dat de betekenis van een naam die wordt gebruikt in een expressiecontext altijd hetzelfde is binnen een blok. Als het bereik van een lokale variabele alleen zou worden uitgebreid van de declaratie tot het einde van het blok, zou in het bovenstaande voorbeeld de eerste toewijzing worden toegewezen aan de instantievariabele en de tweede toewijzing zou worden toegewezen aan de lokale variabele, wat mogelijk leidt tot compileertijdfouten als de instructies van het blok later opnieuw zouden worden gerangschikt.)
De betekenis van een naam binnen een blok kan verschillen op basis van de context waarin de naam wordt gebruikt. In het voorbeeld
class A {} class Test { static void Main() { string A = "hello, world"; string s = A; // expression context Type t = typeof(A); // type context Console.WriteLine(s); // writes "hello, world" Console.WriteLine(t); // writes "A" } }de naam
Awordt gebruikt in een expressiecontext om te verwijzen naar de lokale variabeleAen in een typecontext om naar de klasseAte verwijzen.eindnotitie
Zoals beschreven in §7.1.3, worden brontokens op het hoogste niveau ingesloten door de gegenereerde invoerpuntmethode.
Als de algemene naamruimte eenmaal is bereikt, wordt eerst geprobeerd de naam binnen de gegenereerde invoerpuntmethode te evalueren en alleen als deze poging mislukt, de evaluatie binnen de globale naamruimtedeclaratie wordt uitgevoerd.
Dit kan leiden tot naamschaduwing van naamruimten en typen die zijn gedeclareerd in de globale naamruimte en tot schaduw van geïmporteerde namen.
Als de eenvoudige naamevaluatie buiten de instructies op het hoogste niveau plaatsvindt en de evaluatie een lokale variabele of functie op het hoogste niveau oplevert, resulteert een foutresultaten in de compilatietijd.
7.6.2 Naam verbergen
7.6.2.1 Algemeen
Het bereik van een entiteit omvat doorgaans meer programmatekst dan de declaratieruimte van de entiteit. Het bereik van een entiteit kan met name declaraties bevatten die nieuwe declaratieruimten met entiteiten met dezelfde naam introduceren. Dergelijke declaraties zorgen ervoor dat de oorspronkelijke entiteit verborgen wordt. Omgekeerd wordt gezegd dat een entiteit zichtbaar is wanneer deze niet verborgen is.
Naamverberging treedt op wanneer omgevingen overlappen door nesten en wanneer omgevingen overlappen door overerving. De kenmerken van de twee typen verbergen worden beschreven in de volgende subclauses.
7.6.2.2 Verbergen door nesten
Naamverberging kan optreden als gevolg van het nesten van naamruimten of typen binnen naamruimten, als gevolg van typen die genest zijn binnen klassen of structs, als gevolg van een lokale functie of lambda, en als gevolg van parameter-, lokale variabele- en lokale constanten-declaraties.
Voorbeeld: In de volgende code
class A { int i = 0; void F() { int i = 1; void M1() { float i = 1.0f; Func<double, double> doubler = (double i) => i * 2.0; } } void G() { i = 1; } }binnen de
Fmethode wordt de exemplaarvariabeleiverborgen door de lokale variabelei, maar binnen deGmethodeiverwijst deze nog steeds naar de instantievariabele. Binnen de lokale functieM1verbergt defloat ide directe buitensteifunctie. De lambdaparameteriverbergt defloat ibinnenkant van het lambda-lichaam.eindvoorbeeld
Wanneer een naam in een binnenbereik een naam in een buitenste bereik verbergt, worden alle overbelaste exemplaren van die naam verborgen.
Voorbeeld: In de volgende code
class Outer { static void F(int i) {} static void F(string s) {} class Inner { static void F(long l) {} void G() { F(1); // Invokes Outer.Inner.F F("Hello"); // Error } } }De oproep
F(1)roept deFgedeclareerd inInneraan omdat alle buitenste voorkomens vanFverborgen zijn door de binnenste declaratie. Om dezelfde reden resulteert de aanroepF("Hello")in een compilatietijdfout.eindvoorbeeld
7.6.2.3 Verbergen door overname
De naam die wordt verborgen door overname, treedt op wanneer klassen of structs namen opnieuw declareren die zijn overgenomen van basisklassen. Dit type verbergen van namen neemt een van de volgende vormen aan:
- Een constante, veld, eigenschap, gebeurtenis of type die is geïntroduceerd in een klasse, struct of interface verbergt alle leden van de basisklasse met dezelfde naam.
- Een methode die is geïntroduceerd in een klasse, struct of interface verbergt alle niet-methode basisklasseleden met dezelfde naam en alle basisklassemethoden met dezelfde handtekening (§7.5).
- Een indexeerfunctie die is geïntroduceerd in een klasse, struct of interface verbergt alle indexeerfuncties van het basistype met dezelfde handtekening (§7.5).
De regels voor operatordeclaraties (§15.10) maken het onmogelijk voor een afgeleide klasse om een operator met dezelfde handtekening als een operator in een basisklasse te declareren. Operators verbergen elkaar dus nooit.
In tegenstelling tot het verbergen van een naam uit een buitenste scope, zorgt het verbergen van een zichtbare naam uit een geërfde scope ervoor dat er een waarschuwing wordt gerapporteerd.
Voorbeeld: In de volgende code
class Base { public void F() {} } class Derived : Base { public void F() {} // Warning, hiding an inherited name }de verklaring van
FinDerivedveroorzaakt dat er een waarschuwing wordt gemeld. Het verbergen van een overgenomen naam is specifiek geen fout, omdat hierdoor afzonderlijke evolutie van basisklassen wordt uitgesloten. De bovenstaande situatie kan bijvoorbeeld ontstaan omdat een latere versie van het geïntroduceerdeBaseeenFmethode heeft geïntroduceerd die niet aanwezig was in een eerdere versie van de klasse.eindvoorbeeld
De waarschuwing die wordt veroorzaakt door het verbergen van een overgenomen naam, kan worden geëlimineerd door het gebruik van de new wijzigingsfunctie:
Voorbeeld:
class Base { public void F() {} } class Derived : Base { public new void F() {} }De
newwijzigingsfunctie geeft aan dat deFinDerived'nieuw' is en dat het inderdaad is bedoeld om het overgenomen lid te verbergen.eindvoorbeeld
Een declaratie van een nieuw lid verbergt een overgenomen lid alleen binnen het bereik van het nieuwe lid.
Voorbeeld:
class Base { public static void F() {} } class Derived : Base { private new static void F() {} // Hides Base.F in Derived only } class MoreDerived : Derived { static void G() { F(); // Invokes Base.F } }In het bovenstaande voorbeeld verbergt de declaratie van
FinDeriveddeFdie werd geërfd vanBase, maar aangezien de nieuweFinDerivedprivétoegang heeft, wordt het bereik niet uitgebreid naarMoreDerived. De aanroepF()inMoreDerived.Gis dus geldig en zalBase.Foproepen.eindvoorbeeld
7.7 Naamruimte en typenamen
7.7.1 Algemeen
Voor verschillende contexten in een C#-programma moet een namespace_name of een type_name worden opgegeven.
namespace_name
: namespace_or_type_name
;
type_name
: namespace_or_type_name
;
namespace_or_type_name
: identifier type_argument_list? ('.' identifier type_argument_list?)*
| qualified_alias_member ('.' identifier type_argument_list?)*
;
Een namespace_name is een namespace_or_type_name die verwijst naar een naamruimte.
Na resolutie zoals hieronder beschreven is, moet de namespace_or_type_name van een namespace_name verwijzen naar een naamruimte, of anders treedt er een compilatietijdfout op. Er kunnen geen typeargumenten (§8.4.2) aanwezig zijn in een namespace_name (alleen typen kunnen typeargumenten hebben).
Een type_name is een namespace_or_type_name die verwijst naar een type.
Na de hieronder beschreven resolutie, moet de namespace_or_type_name van een type_name naar een type verwijzen, anders treedt er een compilatiefout op.
Een namespace_or_type_name verwijst naar een type of een naamruimte. Oplossing voor een bepaalde naamruimte of een bepaald type omvat twee stappen op basis van het delen van de grammatica in een voorlooponderdeel, een van de grammaticafragmenten:
identifier type_argument_list?qualified_alias_member
en een volgonderdeel, het grammaticafragment:
('.' identifier type_argument_list?)*
Eerst wordt het voorlooponderdeel opgelost om de beginnende naamruimte of het type te bepalen R₀.
Als het voorloopgedeelte van de namespace_or_type_name een qualified_alias_member is, is dit R₀ de naamruimte of het type dat wordt geïdentificeerd door de oplossing zoals beschreven in §14.9.1.
Anders heeft het voorlooponderdeel, de id van het grammaticafragment type_argument_list?, een van de volgende vormen:
II<A₁, ..., Aₓ>
waarbij geldt:
-
Iis een enkele identificator; en -
<A₁, ..., Aₓ>is een type_argument_list; wanneer er geen type_argument_list is opgegeven, beschouwxals nul.
R₀ wordt als volgt bepaald:
- Als
xnul is en de namespace_or_type_name wordt weergegeven in een algemene methodedeclaratie (§15.6), maar buiten de kenmerken van de methode-header, en als die declaratie een typeparameter (§15.2.3) met de naamIbevat, verwijst danR₀naar die typeparameter. - Als de namespace_or_type_name wordt weergegeven binnen een typedeclaratie, wordt voor elk exemplaartype
T(§15.3.2) gestart met het exemplaartype van dat typedeclaratie en verdergaan met het exemplaartype van elke klasse, struct of interfacedeclaratie (indien van toepassing):- Als
xnul is en de declaratie vanTeen typeparameter met de naamIbevat,R₀verwijst u naar die typeparameter. - Als de namespace_or_type_name wordt weergegeven in de hoofdtekst van de typedeclaratie en
Tof een van de basistypen een genest toegankelijk type met naamIenxtypeparameters bevat, verwijst u danR₀naar dat type dat is samengesteld met de opgegeven typeargumenten. Als er meer dan één dergelijk type is, wordt het type geselecteerd dat binnen het meer afgeleide type is gedeclareerd. Het is een compilerfout als er geen type meer is afgeleid dan alle andere.Opmerking: niet-type leden (constanten, velden, methoden, eigenschappen, indexeerfuncties, operators, instantieconstructors, finalizers en statische constructors) en typeleden met een ander aantal typeparameters worden genegeerd bij het bepalen van de betekenis van de namespace_or_type_name. eindnotitie
- Anders, voor elke naamruimte
N, beginnend met de naamruimte waarin de namespace_or_type_name voorkomt, vervolgens verder te gaan met elke omsluitende naamruimte (indien van toepassing) en eindigend met de globale naamruimte, worden de volgende stappen geëvalueerd totdat een entiteit wordt gevonden.- Als
xnul is enIde naam van een naamruimte inNis, gaat u als volgt te werk:- Als de locatie waar de namespace_or_type_name plaatsvindt, wordt ingesloten door een naamruimtedeclaratie voor
Nen de naamruimtedeclaratie een extern_alias_directive of using_alias_directive bevat die de naamIkoppelt aan een naamruimte of type, of een naamruimtedeclaratie voorNin het programma een global_using_alias_directive bevat die de naamIkoppelt aan een naamruimte of type, dan is de namespace_or_type_name niet eenduidig en treedt er een compilatietijdfout op. -
R₀Anders verwijst u naar de naamruimte met de naamIinN.
- Als de locatie waar de namespace_or_type_name plaatsvindt, wordt ingesloten door een naamruimtedeclaratie voor
- Anders, als
Neen toegankelijk type met de naamIenxtypeparameters bevat, dan:- Als
xnul is en de locatie waar het namespace_or_type_name plaatsvindt, wordt ingesloten door een naamruimtedeclaratie voorNen bevat de naamruimtedeclaratie een extern_alias_directive of using_alias_directive waarmee de naamIwordt gekoppeld aan een naamruimte of type, of een naamruimtedeclaratie voorNin het programma een global_using_alias_directive die de naamIkoppelt aan een naamruimte of type, dan is de namespace_or_type_name dubbelzinnig en treedt er een compileertijdfout op. -
R₀Anders verwijst u naar het type dat is samengesteld met de opgegeven typeargumenten.
- Als
- Anders, als de locatie waar de namespace_or_type_name zich voordoet, wordt omsloten door een naamruimtedeclaratie voor
N:- Als
xnul is en de naamruimtedeclaratie een extern_alias_directive of using_alias_directive bevat die de naamIkoppelt aan een geïmporteerde naamruimte of -type, of een naamruimtedeclaratie voorNin het programma bevat een global_using_alias_directive die de naamIkoppelt aan een geïmporteerde naamruimte of -type, verwijst danR₀naar die naamruimte of het type. - Als de naamruimten die zijn geïmporteerd door de using_namespace_directives van de naamruimtedeclaratie en de naamruimten en typedeclaraties die zijn geïmporteerd door de global_using_namespace_directives en global_using_static_directivevan een naamruimtedeclaratie
Nvoor in het programma exact één type met naamI- enxtypeparameters bevatten, verwijst danR₀naar dat type dat is samengesteld met de opgegeven typeargumenten. - Anders, als de naamruimten die zijn geïmporteerd door de using_namespace_directives van de naamruimtedeclaratie en de naamruimten en typedeclaraties die zijn geïmporteerd door de global_using_namespace_directives en global_using_static_directivevan een naamruimtedeclaratie voor
Nin het programma meer dan één type met naamI- enxtypeparameters bevatten, is de namespace_or_type_name dubbelzinnig en treedt er een compilatiefout op.
- Als
- Als
- Anders is de namespace_or_type_name niet gedefinieerd en treedt er een compilatietijdfout op.
- Als
Als R₀ succesvol is opgelost, wordt het achterste deel van de namespace_or_type_name opgelost. Het afsluitende grammaticafragment bestaat uit k ≥ 0 herhalingen, waarbij elke herhaling de naamruimte of het type waarnaar wordt verwezen verder oplost.
Als k nul is, is er dus geen volggedeelte, dan wordt de namespace_or_type_name omgezet in R₀.
Anders heeft elke herhaling een van de vormen:
.I.I<A₁, ..., Aₓ>
waarbij I, A en x zijn gedefinieerd zoals hierboven.
Voor elke herhaling n, waarbij 1 ≤ n ≤ k, wordt de resolutie Rₙ bepaald. Dit omvat Rₚ, waarbij p = n - 1, de resolutie van de voorgaande herhaling, wordt vastgesteld als volgt:
- Als
xnul is enRₚverwijst naar een naamruimte enRₚeen geneste naamruimte met een naamIbevat, verwijst u naarRₙdie geneste naamruimte. - Anders, indien
Rₚverwijst naar een naamruimte enRₚeen toegankelijk type bevat met de naamIen typeparametersx, dan verwijstRₙnaar dat type, samengesteld met de opgegeven typeargumenten. -
RₚAls dit niet verwijst naar een (mogelijk samengestelde) klasse, struct of interfacetype enRₚof een van de basistypen een genest toegankelijk type met naamIenxtypeparameters, verwijst danRₙnaar dat type dat is samengesteld met de opgegeven typeargumenten. Als er meer dan één dergelijk type is, wordt het type geselecteerd dat binnen het meer afgeleide type is gedeclareerd. Het is een compilerfout als er geen type meer is afgeleid dan alle andere.Opmerking: Als de betekenis van
T.I, voor een bepaald typeT, wordt bepaald als onderdeel van het omzetten van de basisklassespecificatie vanTdan wordt de directe basisklasse beschouwdTalsobject(§15.2.4.2). eindnotitie - Anders is de namespace_or_type_name ongeldig en treedt er een compilatiefout op.
De resolutie van de namespace_or_type_name is de resolutie van de laatste herhaling, Rₖ.
Een namespace_or_type_name mag alleen verwijzen naar een statische klasse (§15.2.2.4) als
- De namespace_or_type_name is het
Tin een namespace_or_type_name van de vormT.I, of - De namespace_or_type_name is de
Tin een typeof_expression (§12.8.18) van de vormtypeof(T)
Voorbeeld:
interface A { class NestedClass { public static void M() {} } } interface B { class NestedClass { public static void M() {} } } interface C : A, B { public void Test() { NestedClass.M(); } // ambiguity between // A.NestedClass and B.NestedClass }In het bovenstaande voorbeeld is de aanroep naar binnen
NestedClass.M()C.Test()dubbelzinnig tussenB.NestedClass.M()enA.NestedClass.M()omdat geen van beide meer is afgeleid dan de andere. Een expliciete verwijzing naar een vanA.NestedClass.M()beide ofB.NestedClass.M()is vereist om de dubbelzinnigheid op te lossen.eindvoorbeeld
7.7.2 Niet-gekwalificeerde namen
Elke naamruimtedeclaratie en typedeclaratie heeft een niet-gekwalificeerde naam die als volgt wordt bepaald:
- Voor een naamruimtedeclaratie is de niet-gekwalificeerde naam de qualified_identifier die is opgegeven in de declaratie.
- Voor een typedeclaratie zonder type_parameter_list is de niet-gekwalificeerde naam de id die is opgegeven in de declaratie.
- Voor een typedeclaratie met K-typeparameters is de niet-gekwalificeerde naam de identifier die is opgegeven in de declaratie, gevolgd door de generieke_afmetingspecificatie (§12.8.18) voor K-typeparameters.
7.7.3 Volledig gekwalificeerde namen
Elke naamruimte en typedeclaratie hebben een volledig gekwalificeerde naam, die de naamruimte of typedeclaratie uniek identificeert onder alle andere binnen het programma. De volledig gekwalificeerde naam van een naamruimte of typedeclaratie met niet-gekwalificeerde naam N wordt als volgt bepaald:
- Als
Neen lid is van de globale naamruimte, is de volledig gekwalificeerde naamN. - Anders is
S.Nde volledig gekwalificeerde naam , waarSde volledig gekwalificeerde naam van de naamruimte of typedeclaratie waarinNwordt gedeclareerd.
Met andere woorden, de volledig gekwalificeerde naam van N is het volledige hiërarchische pad van identifiers en generic_dimension_specifiers die leiden tot N, beginnend vanuit de globale naamruimte. Omdat elk lid van een naamruimte of type een unieke naam heeft, is de volledig gekwalificeerde naam van een naamruimte of typedeclaratie altijd uniek. Het is een compilatiefout voor dezelfde volledig gekwalificeerde naam om te verwijzen naar twee afzonderlijke entiteiten. Met name:
- Het is een fout voor zowel een naamruimtedeclaratie als een typedeclaratie om dezelfde volledig gekwalificeerde naam te hebben.
- Het is een fout voor twee verschillende typen declaraties om dezelfde volledig gekwalificeerde naam te hebben (bijvoorbeeld als zowel een struct- als klassedeclaratie dezelfde volledig gekwalificeerde naam hebben).
- Het is een fout als een typedeclaratie zonder partial-modifier dezelfde volledig gekwalificeerde naam heeft als een andere typedeclaratie (§15.2.7).
Voorbeeld: In het onderstaande voorbeeld ziet u verschillende naamruimte- en typedeclaraties, samen met de bijbehorende volledig gekwalificeerde namen.
class A {} // A namespace X // X { class B // X.B { class C {} // X.B.C } namespace Y // X.Y { class D {} // X.Y.D } } namespace X.Y // X.Y { class E {} // X.Y.E class G<T> // X.Y.G<> { class H {} // X.Y.G<>.H } class G<S,T> // X.Y.G<,> { class H<U> {} // X.Y.G<,>.H<> } }eindvoorbeeld
7.8 Automatisch geheugenbeheer
C# maakt gebruik van automatisch geheugenbeheer, waardoor ontwikkelaars het geheugen dat door objecten wordt bezet handmatig kunnen toewijzen en vrijmaken. Beleid voor automatisch geheugenbeheer wordt geïmplementeerd door een vuilnisophaler. De levenscyclus van een object voor geheugenbeheer is als volgt:
- Wanneer het object wordt gemaakt, wordt er geheugen toegewezen, wordt de constructor uitgevoerd en wordt het object beschouwd als live.
- Als noch het object, noch een van zijn exemplaarvelden toegankelijk is door een mogelijke voortzetting van de uitvoering, met uitzondering van het uitvoeren van finalizers, wordt het object beschouwd als niet meer in gebruik en komt het in aanmerking voor finalisatie.
Opmerking: De C#-compiler en de garbagecollector kunnen ervoor kiezen om code te analyseren om te bepalen welke verwijzingen naar een object in de toekomst kunnen worden gebruikt. Als een lokale variabele die binnen het bereik valt bijvoorbeeld de enige bestaande verwijzing naar een object is, maar die lokale variabele nooit wordt verwezen in een mogelijke voortzetting van de uitvoering vanaf het huidige uitvoeringspunt in de procedure, kan de garbagecollector het object behandelen als niet meer in gebruik. eindnotitie
- Zodra het object in aanmerking komt voor finalisatie, wordt op een later tijdstip de finalizer (§15.13) voor het object uitgevoerd, indien van toepassing. Onder normale omstandigheden wordt de finalizer voor het object slechts één keer uitgevoerd, hoewel implementatiegedefinieerde API's dit gedrag kunnen toestaan om te worden overschreven.
- Zodra de finalizer voor een object wordt uitgevoerd, wordt het object, als het object of een van zijn exemplaarvelden niet toegankelijk is door een mogelijke voortzetting van de uitvoering, met inbegrip van het uitvoeren van finalizers, als ontoegankelijk beschouwd en komt het in aanmerking voor verzameling.
Opmerking: Een object dat eerder niet kon worden geopend, is mogelijk opnieuw toegankelijk vanwege de finalizer. Hieronder ziet u een voorbeeld hiervan. eindnotitie
- Ten slotte, op een bepaald moment nadat het object in aanmerking komt voor verzameling, maakt de garbagecollector het geheugen vrij dat aan dat object is gekoppeld.
De garbagecollector onderhoudt informatie over het gebruik van objecten en gebruikt deze informatie om beslissingen te nemen over geheugenbeheer, zoals waar in het geheugen een nieuw gemaakt object moet worden gevonden, wanneer een object moet worden verplaatst en wanneer een object niet meer wordt gebruikt of niet meer toegankelijk is.
Net als andere talen die ervan uitgaan dat er een garbagecollector bestaat, is C# zo ontworpen dat de garbagecollection een breed scala aan beleidsregels voor geheugenbeheer kan implementeren. C# specificeert geen tijdsbeperking binnen die periode, noch een volgorde waarin finalizers worden uitgevoerd. Of finalizers al dan niet worden uitgevoerd als onderdeel van de beëindiging van de toepassing, is door de implementatie gedefinieerd (§7.1).
Het gedrag van de garbagecollector kan in zekere mate worden beheerd via statische methoden in de klasse System.GC. Deze klasse kan worden gebruikt om te verzoeken dat er een verzameling plaatsvindt, dat finalizers worden uitgevoerd (of niet worden uitgevoerd), enzovoort.
Voorbeeld: Omdat de garbagecollector brede breedtegraad heeft bij het bepalen wanneer objecten moeten worden verzameld en finalizers moeten worden uitgevoerd, kan een conforme implementatie uitvoer produceren die verschilt van de uitvoer die wordt weergegeven door de volgende code. Het programma
class A { ~A() { Console.WriteLine("Finalize instance of A"); } } class B { object Ref; public B(object o) { Ref = o; } ~B() { Console.WriteLine("Finalize instance of B"); } } class Test { static void Main() { B b = new B(new A()); b = null; GC.Collect(); GC.WaitForPendingFinalizers(); } }maakt een exemplaar van klasse
Aen een exemplaar van klasseB. Deze objecten komen in aanmerking voor garbagecollection wanneer de variabelebde waardenullwordt toegewezen, omdat het na deze tijd onmogelijk is voor elke door de gebruiker geschreven code om ze te openen. De uitvoer kan een van beide zijnFinalize instance of A Finalize instance of Bof
Finalize instance of B Finalize instance of Aomdat de taal geen beperkingen oplegt voor de volgorde waarin objecten afval worden verzameld.
In subtiele gevallen kan het onderscheid tussen 'in aanmerking komen voor finalisatie' en 'in aanmerking komen voor verzameling' belangrijk zijn. Bijvoorbeeld:
class A { ~A() { Console.WriteLine("Finalize instance of A"); } public void F() { Console.WriteLine("A.F"); Test.RefA = this; } } class B { public A Ref; ~B() { Console.WriteLine("Finalize instance of B"); Ref.F(); } } class Test { public static A RefA; public static B RefB; static void Main() { RefB = new B(); RefA = new A(); RefB.Ref = RefA; RefB = null; RefA = null; // A and B now eligible for finalization GC.Collect(); GC.WaitForPendingFinalizers(); // B now eligible for collection, but A is not if (RefA != null) { Console.WriteLine("RefA is not null"); } } }In het bovenstaande programma, als de garbagecollector ervoor kiest om de finalizer van
Avóór de finalizer vanBuit te voeren, kan de uitvoer van dit programma zijn:Finalize instance of A Finalize instance of B A.F RefA is not nullMerk op dat hoewel het exemplaar van
Aniet in gebruik was en de finalizer vanAwerd uitgevoerd, het nog steeds mogelijk is om methoden vanA(in dit gevalF) aan te roepen vanuit een andere finalizer. Houd er ook rekening mee dat het uitvoeren van een finalizer ertoe kan leiden dat een object opnieuw bruikbaar wordt vanuit het mainlineprogramma. In dit geval heeft het uitvoeren vanBde finalizer ertoe geleid dat een exemplaar vanAdie eerder niet in gebruik was, toegankelijk werd vanuit de live referenceTest.RefA. Na de aanroep naarWaitForPendingFinalizers, komt het exemplaar vanBin aanmerking voor verzameling, maar het exemplaarAvan is niet, vanwege de verwijzingTest.RefA.eindvoorbeeld
7.9 Uitvoeringsvolgorde
De uitvoering van een C#-programma verloopt zodanig dat de bijwerkingen van elke uitvoeringsthread behouden blijven op kritieke uitvoeringspunten. Een neveneffect wordt gedefinieerd als een lees- of schrijfbewerking van een vluchtig veld, een schrijfbewerking naar een niet-vluchtige variabele, een schrijfbewerking naar een externe resource en het genereren van een uitzondering. De kritieke uitvoeringspunten waarop de volgorde van deze bijwerkingen behouden blijft, zijn verwijzingen naar vluchtige velden (§15.5.4), lock instructies (§13.13) en het maken en beëindigen van threads. De uitvoeringsomgeving is gratis om de volgorde van uitvoering van een C#-programma te wijzigen, afhankelijk van de volgende beperkingen:
- De gegevensafhankelijkheid blijft behouden binnen een uitvoeringsdraad. Dat wil gezegd, de waarde van elke variabele wordt berekend alsof alle instructies in de thread in de oorspronkelijke programmavolgorde zijn uitgevoerd.
- Regels voor initialisatievolgorde blijven behouden (§15.5.5, §15.5.6).
- De volgorde van bijwerkingen blijft behouden met betrekking tot vluchtige lees- en schrijfbewerkingen (§15.5.4). Daarnaast hoeft de uitvoeringsomgeving geen deel van een expressie te evalueren als deze kan afleiden dat de waarde van die expressie niet wordt gebruikt en dat er geen benodigde bijwerkingen worden geproduceerd (inclusief eventuele oorzaken die worden veroorzaakt door het aanroepen van een methode of het openen van een vluchtig veld). Wanneer de uitvoering van het programma wordt onderbroken door een asynchrone gebeurtenis (zoals een uitzondering die door een andere thread wordt gegenereerd), is het niet gegarandeerd dat de waarneembare bijwerkingen zichtbaar zijn in de oorspronkelijke programmavolgorde.
ECMA C# draft specification