Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
7.1 Programstart
Ett program kan kompileras antingen som ett klassbibliotek som ska användas som en del av andra program eller som ett program som kan startas direkt. Mekanismen för att fastställa det här kompileringsläget är implementeringsdefinierad och extern med den här specifikationen.
Ett program som sammanställts som en ansökan ska innehålla minst en metod som kvalificerar sig som startpunkt genom att uppfylla följande krav:
- Den ska ha namnet Main.
- Det ska vara static.
- Den får inte vara generisk.
- Den skall deklareras i en icke-generisk typ. Om den typ som deklarerar metoden är en kapslad typ kan ingen av dess omslutande typer vara generiska.
- Det kan ha asyncmodifieraren förutsatt att metodens returtyp ärSystem.Threading.Tasks.TaskellerSystem.Threading.Tasks.Task<int>.
- Returtypen ska vara void,int,System.Threading.Tasks.TaskellerSystem.Threading.Tasks.Task<int>.
- Det får inte vara en partiell metod (§15.6.9) utan genomförande.
- Parameterlistan ska antingen vara tom eller ha en enda värdeparameter av typen string[].
Obs! Metoder med
asyncmodifieraren måste ha exakt en av de två returtyper som anges ovan för att kvalificeras som en startpunkt. Enasync voidmetod, eller enasyncmetod som returnerar en annan väntande typ, tillValueTaskexempel ellerValueTask<int>inte kvalificerar sig som en startpunkt. slutkommentar
Om mer än en metod som kvalificerar sig som startpunkt deklareras inom ett program, kan en extern mekanism användas för att ange vilken metod som anses vara den faktiska startpunkten för programmet. Om en kvalificerande metod som har en returtyp av int eller void hittas, anses alla kvalificerande metoder som har en returtyp av System.Threading.Tasks.Task eller System.Threading.Tasks.Task<int> inte vara en startpunktsmetod. Det är ett kompileringsfel för ett program som ska kompileras som ett program utan exakt en startpunkt. Ett program som kompilerats som ett klassbibliotek kan innehålla metoder som skulle kvalificera sig som startpunkter för programmet, men det resulterande biblioteket har ingen startpunkt.
Normalt bestäms den deklarerade tillgängligheten (§7.5.2) av en metod av de åtkomstmodifierare (§15.3.6) som anges i deklarationen, och på samma sätt bestäms den deklarerade tillgängligheten av en typ av åtkomstmodifierare som anges i deklarationen. För att en viss metod av en viss typ ska kunna anropas ska både typen och medlemmen vara tillgängliga. Startpunkten för programmet är dock ett specialfall. Mer specifikt kan körningsmiljön komma åt programmets startpunkt oavsett dess deklarerade tillgänglighet och oavsett den deklarerade tillgängligheten för dess omslutande typdeklarationer.
När startpunktsmetoden har en returtyp av System.Threading.Tasks.Task eller System.Threading.Tasks.Task<int>ska en kompilator syntetisera en synkron startpunktsmetod som anropar motsvarande Main metod. Den syntetiserade metoden har parametrar och returtyper baserat på Main metoden:
- Parameterlistan för den syntetiserade metoden är densamma som parameterlistan för Mainmetoden
- Om returtypen för Mainmetoden ärSystem.Threading.Tasks.Taskär returtypen för den syntetiserade metodenvoid
- Om returtypen för Mainmetoden ärSystem.Threading.Tasks.Task<int>är returtypen för den syntetiserade metodenint
Körningen av den syntetiserade metoden fortsätter enligt följande:
- Den syntetiserade metoden anropar Mainmetoden och skickar dessstring[]parametervärde som ett argument omMainmetoden har en sådan parameter.
- 
              MainOm metoden utlöser ett undantag sprids undantaget av den syntetiserade metoden.
- Annars väntar den syntetiserade startpunkten på att den returnerade aktiviteten ska slutföras, anropar GetAwaiter().GetResult()uppgiften med hjälp av antingen metoden parameterlös instans eller tilläggsmetoden som beskrivs av §C.3. Om uppgiften misslyckasGetResult()utlöser ett undantag och det här undantaget sprids av den syntetiserade metoden.
- För en Mainmetod med returtypenSystem.Threading.Tasks.Task<int>, returneras värdet som returneras avintfrån den syntetiserade metoden om aktiviteten har slutförtsGetResult().
Den effektiva startpunkten för ett program är startpunkten som deklareras i programmet, eller den syntetiserade metoden om en krävs enligt beskrivningen ovan. Returtypen för den effektiva startpunkten är därför alltid void eller int.
När ett program körs skapas en ny programdomän. Flera olika instansieringar av ett program kan finnas på samma dator samtidigt och var och en har en egen programdomän. En programdomän möjliggör programisolering genom att fungera som en container för programtillstånd. En programdomän fungerar som en container och gräns för de typer som definierats i programmet och de klassbibliotek som används. Typer som läses in i en programdomän skiljer sig från samma typer som läses in i en annan programdomän, och instanser av objekt delas inte direkt mellan programdomäner. Varje programdomän har till exempel en egen kopia av statiska variabler för dessa typer och en statisk konstruktor för en typ körs högst en gång per programdomän. Implementeringar kan tillhandahålla implementeringsdefinierade principer eller mekanismer för att skapa och förstöra programdomäner.
Programstart inträffar när körningsmiljön anropar programmets effektiva startpunkt. Om den effektiva startpunkten deklarerar en parameter ska implementeringen under programstarten se till att det ursprungliga värdet för parametern är en icke-null-referens till en strängmatris. Den här matrisen ska bestå av icke-null-referenser till strängar, så kallade programparametrar, som ges implementeringsdefinierade värden av värdmiljön innan programmet startas. Avsikten är att tillhandahålla den programinformation som fastställdes innan programmet startas från någon annanstans i den värdbaserade miljön.
Obs! På system som stöder en kommandorad motsvarar programparametrar vad som vanligtvis kallas kommandoradsargument. slutkommentar
Om den effektiva startpunktens returtyp är intanvänds returvärdet från metodanropet av körningsmiljön i programavslutning (§7.2).
Förutom de situationer som anges ovan beter sig startpunktsmetoder som de som inte är startpunkter i alla avseenden. I synnerhet om startpunkten anropas vid någon annan tidpunkt under programmets livslängd, till exempel med vanlig metodanrop, finns det ingen särskild hantering av metoden: om det finns en parameter kan den ha ett initialt värde på null, eller ett icke-värdenull som refererar till en matris som innehåller null-referenser. På samma sätt har returvärdet för startpunkten ingen särskild betydelse förutom i anropet från körningsmiljön.
7.2 Programavslut
Programavslut returnerar kontrollen till körningsmiljön.
Om returtypen för programmets effektiva startpunktsmetod är int och körningen slutförs utan att resultera i ett undantag, fungerar värdet för den int returnerade som programmets avslutningsstatuskod. Syftet med den här koden är att tillåta kommunikation av lyckad eller misslyckad körningsmiljö. Om returtypen för den effektiva startpunktsmetoden är void och körningen slutförs utan att resultera i ett undantag är 0statuskoden för avslutning .
Om den effektiva startpunktsmetoden avslutas på grund av ett undantag (§22.4) är slutkoden implementeringsdefinierad. Dessutom kan implementeringen tillhandahålla alternativa API:er för att ange slutkoden.
Huruvida finalizers (§15.13) körs som en del av programavslutet är implementeringsdefinierat.
Obs! .NET Framework-implementeringen gör allt för att anropa slutförare (§15.13) för alla objekt som ännu inte har skräpinsamling, såvida inte sådan rensning har undertryckts (till exempel genom ett anrop till biblioteksmetoden
GC.SuppressFinalize). slutkommentar
7.3 Förklaringar
Deklarationer i ett C#-program definierar programmets element. C#-program ordnas med hjälp av namnområden. Dessa introduceras med hjälp av namnområdesdeklarationer (§14), som kan innehålla typdeklarationer och kapslade namnområdesdeklarationer. Typdeklarationer (§14.7) används för att definiera klasser (§15), structs (§16), gränssnitt (§19), uppräkningar (§20) och delegater (§21). Vilka typer av medlemmar som tillåts i en typdeklaration beror på typdeklarationens form. Till exempel klassdeklarationer kan innehålla deklarationer för konstanter (§15.4), fält (§15.5), metoder (§15.6), egenskaper (§15.7), händelser (§15.8), indexerare (§15.9 ), operatorer (§15.10), instanskonstruktorer (§15.11), statiska konstruktorer (§15.12), finalizers (§15.13) och kapslade typer (§15.3.9).
En deklaration definierar ett namn i det deklarationsutrymme som deklarationen tillhör. Det är ett kompileringsfel att ha två eller flera deklarationer som introducerar medlemmar med samma namn i ett deklarationsutrymme, förutom i följande fall:
- Två eller flera namnområdesdeklarationer med samma namn tillåts i samma deklarationsutrymme. Sådana namnområdesdeklarationer aggregeras för att bilda ett enda logiskt namnområde och dela ett enda deklarationsutrymme.
- Deklarationer i separata program men i samma namnområdesdeklarationsutrymme tillåts dela samma namn.
Obs! Dessa deklarationer kan dock medföra tvetydigheter om de ingår i samma program. slutkommentar 
- Två eller flera metoder med samma namn men distinkta signaturer tillåts i samma deklarationsutrymme (§7.6).
- Två eller flera typdeklarationer med samma namn men distinkta tal av typparametrar tillåts i samma deklarationsutrymme (§7.8.2).
- Två eller flera typdeklarationer med den partiella modifieraren i samma deklarationsutrymme kan dela samma namn, samma antal typparametrar och samma klassificering (klass, struct eller gränssnitt). I det här fallet bidrar typdeklarationerna till en enda typ och aggregeras själva för att bilda ett enda deklarationsutrymme (§15.2.7).
- En namnområdesdeklaration och en typdeklaration i samma deklarationsutrymme kan dela samma namn så länge typdeklarationen har minst en typparameter (§7.8.2).
Det finns flera olika typer av deklarationsutrymmen, enligt beskrivningen i följande.
- Inom alla kompileringsenheter i ett program är namespace_member_declaration utan omslutande namespace_declaration medlemmar i ett enda kombinerat deklarationsutrymme som kallas globalt deklarationsutrymme.
- Inom alla kompileringsenheter i ett program är namespace_member_declarations inom namespace_declarationsom har samma fullständigt kvalificerade namnområdesnamn medlemmar i ett enda kombinerat deklarationsutrymme.
- Varje compilation_unit och namespace_body har ett aliasdeklarationsutrymme. Varje extern_alias_directive och using_alias_directive av compilation_unit eller namespace_body bidrar en medlem till aliasdeklarationsutrymmet (§14.5.2).
- Varje icke-partiell klass-, struct- eller gränssnittsdeklaration skapar ett nytt deklarationsutrymme. Varje partiell klass, struct eller gränssnittsdeklaration bidrar till ett deklarationsutrymme som delas av alla matchande delar i samma program (§16.2.4). Namn introduceras i det här deklarationsutrymmet via class_member_declarations, struct_member_declarations, interface_member_declarations eller type_parameters. Med undantag för överlagrade instanskonstruktordeklarationer och statiska konstruktordeklarationer får en klass, struct eller gränssnitt inte innehålla en medlemsdeklaration med samma namn som klassen, struct eller gränssnittet. En klass, struct eller ett gränssnitt tillåter deklaration av överlagrade metoder och indexerare. Dessutom tillåter en klass eller struct deklaration av överlagrade instanskonstruktorer och operatorer. Till exempel kan en klass, struct eller ett gränssnitt innehålla flera metoddeklarationer med samma namn, förutsatt att dessa metoddeklarationer skiljer sig åt i signaturen (§7.6). Observera att basklasser inte bidrar till deklarationsutrymmet för en klass, och basgränssnitt bidrar inte till deklarationsutrymmet i ett gränssnitt. Därför kan en härledd klass eller ett gränssnitt deklarera en medlem med samma namn som en ärvd medlem. En sådan medlem sägs dölja den ärvda medlemmen.
- Varje ombudsdeklaration skapar ett nytt deklarationsutrymme. Namn introduceras i det här deklarationsutrymmet via parametrar (fixed_parameters och parameter_arrays) och type_parameters.
- Varje uppräkningsdeklaration skapar ett nytt deklarationsutrymme. Namn introduceras i det här deklarationsutrymmet via enum_member_declarations.
- Varje metoddeklaration, egenskapsdeklaration, egenskapsåtkomstdeklaration, indexeringsdeklaration, deklaration av indexerare, operatordeklaration, instanskonstruktordeklaration, anonym funktion och lokal funktion skapar ett nytt deklarationsutrymme som kallas för ett lokalt variabeldeklarationsutrymme. Namn introduceras i det här deklarationsutrymmet via parametrar (fixed_parameters och parameter_arrays) och type_parameters. Set-accessorn för en egenskap eller en indexerare introducerar namnet valuesom en parameter. Brödtexten för funktionsmedlemmen, den anonyma funktionen eller den lokala funktionen anses vara kapslad i det lokala variabeldeklarationsutrymmet. När ett lokalt variabeldeklarationsutrymme och ett kapslat lokalt variabeldeklarationsutrymme innehåller element med samma namn, inom omfånget för det kapslade lokala namnet, döljs det yttre lokala namnet (§7.7.1) med det kapslade lokala namnet.
- Ytterligare deklarationsutrymmen för lokala variabler kan förekomma i medlemsdeklarationer, anonyma funktioner och lokala funktioner. Namn introduceras i dessa deklarationsutrymmen genom mönster s, declaration_expressions, declaration_statements och exception_specifiers. Lokala variabeldeklarationsutrymmen kan kapslas, men det är ett fel för ett lokalt variabeldeklarationsutrymme och ett kapslat lokalt variabeldeklarationsutrymme som innehåller element med samma namn. Inom ett kapslat deklarationsutrymme går det därför inte att deklarera en lokal variabel, lokal funktion eller konstant med samma namn som en parameter, typparameter, lokal variabel, lokal funktion eller konstant i ett omslutande deklarationsutrymme. Det är möjligt att två deklarationsutrymmen innehåller element med samma namn så länge inget av deklarationsutrymmena innehåller det andra. Lokala deklarationsutrymmen skapas av följande konstruktioner: - Varje variable_initializer i ett fält och en egenskapsdeklaration introducerar ett eget lokalt variabeldeklarationsutrymme som inte är kapslat i något annat lokalt variabeldeklarationsutrymme.
- Brödtexten för en funktionsmedlem, anonym funktion eller lokal funktion, om någon, skapar ett lokalt variabeldeklarationsutrymme som anses vara kapslat i funktionens lokala variabeldeklarationsutrymme.
- Varje constructor_initializer skapar ett lokalt variabeldeklarationsutrymme kapslat i instanskonstruktordeklarationen. Det lokala variabeldeklarationsutrymmet för konstruktorns brödtext är i sin tur kapslat i det här lokala variabeldeklarationsutrymmet.
- Varje block, switch_block, specific_catch_clause, iteration_statement och using_statement skapar ett kapslat lokalt variabeldeklarationsutrymme.
- Varje embedded_statement som inte är direkt en del av en statement_list skapar ett kapslat lokalt variabeldeklarationsutrymme.
- Varje switch_section skapar ett kapslat lokalt variabeldeklarationsutrymme. Variabler som deklareras direkt inom statement_list i switch_section (men inte inom ett kapslat lokalt variabeldeklarationsutrymme i statement_list) läggs dock direkt till i det lokala variabeldeklarationsutrymmet för den omslutande switch_block i stället för switch_section.
- Den syntaktiska översättningen av en query_expression (§12.21.3) kan introducera ett eller flera lambda-uttryck. Som anonyma funktioner skapar var och en av dessa ett lokalt variabeldeklarationsutrymme enligt beskrivningen ovan.
 
- Varje block eller switch_block skapar ett separat deklarationsutrymme för etiketter. Namn introduceras i det här deklarationsutrymmet via labeled_statements och namnen refereras via goto_statements. Etikettdeklarationsutrymmet för ett block innehåller alla kapslade block. Inom ett kapslat block går det därför inte att deklarera en etikett med samma namn som en etikett i ett omslutande block.
Obs! Det faktum att variabler som deklareras direkt inom en switch_section läggs till i det lokala variabeldeklarationsutrymmet för switch_block i stället för switch_section kan leda till överraskande kod. I exemplet nedan finns den lokala variabeln
yi omfånget i växelavsnittet för standardfallet, trots att deklarationen visas i växelavsnittet för fall 0. Den lokala variabelnzfinns inte i omfånget i switchavsnittet för standardfallet, eftersom den introduceras i det lokala variabeldeklarationsutrymmet för växelavsnittet där deklarationen inträffar.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; }slutkommentar
Den textordning i vilken namn deklareras har i allmänhet ingen betydelse. I synnerhet är textordningen inte betydande för deklarationen och användningen av namnrymder, konstanter, metoder, egenskaper, händelser, indexerare, operatorer, instanskonstruktorer, finalizers, statiska konstruktorer och typer. Deklarationsordningen är betydande på följande sätt:
- Deklarationsordning för fältdeklarationer bestämmer i vilken ordning deras initialiserare (om några) körs (§15.5.6.2, §15.5.6.3).
- Lokala variabler ska definieras innan de används (§7.7).
- Deklarationsordningen för uppräkningsmedlemsdeklarationer (§20.4) är betydande när constant_expression värden utelämnas.
Exempel: Deklarationsutrymmet för ett namnområde är "öppet avslutat" och två namnområdesdeklarationer med samma fullständigt kvalificerade namn bidrar till samma deklarationsutrymme. Till exempel
namespace Megacorp.Data { class Customer { ... } } namespace Megacorp.Data { class Order { ... } }De två namnområdesdeklarationerna ovan bidrar till samma deklarationsutrymme, i det här fallet deklarerar du två klasser med de fullständigt kvalificerade namnen
Megacorp.Data.CustomerochMegacorp.Data.Order. Eftersom de två deklarationerna bidrar till samma deklarationsutrymme skulle det ha orsakat ett kompileringsfel om var och en innehöll en deklaration av en klass med samma namn.slutexempel
Obs! Som anges ovan innehåller deklarationsutrymmet för ett block alla kapslade block. I följande exempel
Fresulterar metoderna ochGdärför i ett kompileringsfel eftersom namnetideklareras i det yttre blocket och inte kan redeclared i det inre blocket. Metoderna ochHär dockIgiltiga eftersom de tvåideklareras i separata icke-kapslade block.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(); } } }slutkommentar
7.4 Medlemmar
7.4.1 Allmänt
Namnområden och typer har medlemmar.
Obs! Medlemmarna i en entitet är allmänt tillgängliga med hjälp av ett kvalificerat namn som börjar med en referens till entiteten, följt av en "
." token, följt av namnet på medlemmen. slutkommentar
Medlemmar av en typ deklareras antingen i typdeklarationen eller ärvs från basklassen av typen. När en typ ärver från en basklass blir alla medlemmar i basklassen, förutom instanskonstruktorer, finalizers och statiska konstruktorer medlemmar av den härledda typen. Den deklarerade tillgängligheten för en basklassmedlem styr inte om medlemmen ärvs – arv utökas till alla medlemmar som inte är en instanskonstruktor, statisk konstruktor eller slutförare.
Obs! En ärvd medlem kan dock inte vara tillgänglig i en härledd typ, till exempel på grund av dess deklarerade tillgänglighet (§7.5.2). slutkommentar
7.4.2 Namnområdesmedlemmar
Namnområden och typer som inte har någon omslutande namnrymd är medlemmar i det globala namnområdet. Detta motsvarar direkt de namn som deklarerats i det globala deklarationsutrymmet.
Namnområden och typer som deklareras i ett namnområde är medlemmar i det namnområdet. Detta motsvarar direkt de namn som deklarerats i namnområdets deklarationsutrymme.
Namnområden har inga åtkomstbegränsningar. Det går inte att deklarera privata, skyddade eller interna namnområden och namnområdesnamn är alltid offentligt tillgängliga.
7.4.3 Struct medlemmar
Medlemmarna i en struct är de medlemmar som deklareras i structen och de medlemmar som ärvts från structens direkta basklass System.ValueType och den indirekta basklassen object.
Medlemmarna av en enkel typ motsvarar direkt medlemmarna av den structtyp som aliaseras av den enkla typen (§8.3.5).
7.4.4 Uppräkningsmedlemmar
Medlemmarna i en uppräkning är de konstanter som deklareras i uppräkningen och de medlemmar som ärvts från uppräkningens direkta basklass System.Enum och de indirekta basklasserna System.ValueType och object.
7.4.5 Klassmedlemmar
Medlemmarna i en klass är de medlemmar som deklareras i klassen och de medlemmar som ärvts från basklassen (förutom klassen object som inte har någon basklass). Medlemmarna som ärvs från basklassen inkluderar konstanter, fält, metoder, egenskaper, händelser, indexerare, operatorer och typer av basklassen, men inte instanskonstruktorer, finalizers och statiska konstruktorer för basklassen. Basklassmedlemmar ärvs utan hänsyn till deras tillgänglighet.
En klassdeklaration kan innehålla deklarationer av konstanter, fält, metoder, egenskaper, händelser, indexerare, operatorer, instanskonstruktorer, finalizers, statiska konstruktorer och typer.
Medlemmarna av object (§8.2.3) och string (§8.2.5) motsvarar direkt till medlemmarna av klassificeratyperna som de alias.
7.4.6 Gränssnittsmedlemmar
Medlemmarna i ett gränssnitt är de medlemmar som deklareras i gränssnittet och i alla basgränssnitt i gränssnittet.
Obs! Medlemmarna i klassen
objectär inte strikt talande medlemmar i något gränssnitt (§19.4). Medlemmarna i klassenobjectär dock tillgängliga via medlemssökning i alla gränssnittstyper (§12.5). slutkommentar
7.4.7 Matrismedlemmar
Medlemmarna i en matris är de medlemmar som ärvs från klassen System.Array.
7.4.8 Delegera medlemmar
Ett ombud ärver medlemmar från klassen System.Delegate. Dessutom innehåller den en metod med namnet Invoke med samma returtyp och parameterlista som anges i deklarationen (§21.2). En anrop av denna metod ska bete sig identiskt med ett ombudsanrop (§21.6) på samma delegatinstans.
En implementering kan ge ytterligare medlemmar, antingen genom arv eller direkt i själva ombudet.
7.5 Medlemsåtkomst
7.5.1 Allmänt
Medlemsdeklarationer tillåter kontroll över medlemsåtkomst. Tillgängligheten för en medlem upprättas genom den deklarerade tillgängligheten (§7.5.2) av medlemmen i kombination med tillgängligheten av den omedelbart innehållande typen, om någon.
När åtkomst till en viss medlem tillåts sägs medlemmen vara tillgänglig. Omvänt, när åtkomst till en viss medlem är otillåten, sägs medlemmen vara otillgänglig. Åtkomst till en medlem är tillåten när den textplats där åtkomsten äger rum ingår i tillgänglighetsdomänen (§7.5.3) för medlemmen.
7.5.2 Deklarerad tillgänglighet
Den deklarerade tillgängligheten för en medlem kan vara något av följande:
- Offentlig, som väljs genom att inkludera en publicmodifierare i medlemsdeklarationen. Den intuitiva innebörden avpublicär "åtkomsten är inte begränsad".
- Skyddad, som väljs genom att inkludera en protectedmodifierare i medlemsdeklarationen. Den intuitiva innebörden avprotectedär "åtkomst begränsad till den innehållande klassen eller gränssnittet, eller klasser eller gränssnitt som härleds från den innehållande typen".
- Intern, som väljs genom att inkludera en internalmodifierare i medlemsdeklarationen. Den intuitiva innebörden avinternalär "åtkomst begränsad till den här sammansättningen".
- Skyddat internt, som väljs genom att inkludera både en protectedoch eninternalmodifierare i medlemsdeklarationen. Den intuitiva innebörden avprotected internalär "tillgänglig i den här sammansättningen samt typer som härleds från den innehållande klassen".
- Privat skyddat, som väljs genom att inkludera både en privateoch enprotectedmodifierare i medlemsdeklarationen. Den intuitiva innebörden avprivate protectedär "tillgänglig i den här sammansättningen av den innehållande klassen och typer som härleds från den innehållande klassen".
- Privat, som väljs genom att inkludera en privatemodifierare i medlemsdeklarationen. Den intuitiva innebörden avprivateär "åtkomst begränsad till den innehållande typen".
Beroende på i vilken kontext en medlemsdeklaration sker tillåts endast vissa typer av deklarerad tillgänglighet. När en medlemsdeklaration inte innehåller några åtkomstmodifierare avgör dessutom den kontext i vilken deklarationen äger rum standardvärdet för deklarerad tillgänglighet.
- Namnrymder har publicimplicit deklarerat tillgänglighet. Inga åtkomstmodifierare tillåts i namnområdesdeklarationer.
- Typer som deklareras direkt i kompileringsenheter eller namnområden (till skillnad från inom andra typer) kan ha publicellerinternaldeklarerat tillgänglighet och standardvärdet förinternaldeklarerad tillgänglighet.
- Klassmedlemmar kan ha någon av de tillåtna typerna av deklarerad tillgänglighet och standard för privatedeklarerad tillgänglighet.Obs! En typ som deklareras som medlem i en klass kan ha någon av de tillåtna typerna av deklarerad tillgänglighet, medan en typ som deklareras som medlem i ett namnområde bara publickan ha ellerinternaldeklarerat tillgänglighet. slutkommentar
- Struct-medlemmar kan ha public,internalellerprivatedeklarerad tillgänglighet och standardvärdetprivateför deklarerad tillgänglighet eftersom structs är implicit förseglade. Struct-medlemmar som introduceras i enstruct(dvs. inte ärvd av den structen) kan inte haprotected,protected internalellerprivate protecteddeklarerad tillgänglighet.Obs! En typ som deklareras som medlem i en struct kan ha public,internalellerprivatedeklarerad tillgänglighet, medan en typ som deklareras som medlem i ett namnområde barapublickan ha ellerinternaldeklarerat tillgänglighet. slutkommentar
- Gränssnittsmedlemmar har publicimplicit deklarerat tillgänglighet. Inga åtkomstmodifierare tillåts i medlemsdeklarationer för gränssnittet.
- Uppräkningsmedlemmar har publicimplicit deklarerat tillgänglighet. Inga åtkomstmodifierare tillåts i medlemsdeklarationer för uppräkning.
7.5.3 Tillgänglighetsdomäner
Tillgänglighetsdomänen för en medlem består av de (eventuellt åtskilda) avsnitten i programtexten där åtkomst till medlemmen tillåts. För att definiera tillgänglighetsdomänen för en medlem sägs en medlem vara på toppnivå om den inte deklareras inom en typ och en medlem sägs vara kapslad om den deklareras inom en annan typ. Dessutom definieras programtexten i ett program som all text som finns i alla kompileringsenheter i programmet, och programtexten av en typ definieras som all text som finns i type_declarationav den typen (inklusive eventuellt typer som är kapslade inom typen).
Tillgänglighetsdomänen för en fördefinierad typ (till exempel object, inteller double) är obegränsad.
Tillgänglighetsdomänen för en obunden toppnivå (T§8.4.4) som deklareras i ett program P definieras på följande sätt:
- Om den deklarerade tillgängligheten Tför är offentlig är tillgänglighetsdomänenTför programtextenPför och alla program som refererar tillP.
- Om den deklarerade tillgängligheten Tför är intern är tillgänglighetsdomänenTför programtextenP.
Obs! Av dessa definitioner följer att tillgänglighetsdomänen för en obunden toppnivå alltid är minst programtexten för programmet där den typen deklareras. slutkommentar
Tillgänglighetsdomänen för en konstruerad typ T<A₁, ..., Aₑ> är skärningspunkten för tillgänglighetsdomänen för den obundna generiska typen T och tillgänglighetsdomänerna för typargumenten A₁, ..., Aₑ.
Tillgänglighetsdomänen för en kapslad medlem M som deklareras i en typ T i ett program Pdefinieras på följande sätt (notera att M det kan vara en typ):
- Om den deklarerade tillgängligheten Mför ärpublicär tillgänglighetsdomänenMför tillgänglighetsdomänenTför .
- Om den deklarerade tillgängligheten för är Mska viprotected internalvara en union av programtexten förDoch programtexten av alla typer som härletts frånP, som deklareras utanförT.PTillgänglighetsdomänenMför är skärningspunkten för tillgänglighetsdomänenTför medD.
- Om den deklarerade tillgängligheten för är Mska viprivate protectedvara skärningspunkten mellan programtextenDi och programtexten förPoch alla typer som härletts frånT.TTillgänglighetsdomänenMför är skärningspunkten för tillgänglighetsdomänenTför medD.
- Om den deklarerade tillgängligheten för är Mska viprotectedvara en union av programtexten förDoch programtexten av alla typer som härletts frånT.TTillgänglighetsdomänenMför är skärningspunkten för tillgänglighetsdomänenTför medD.
- Om den deklarerade tillgängligheten Mför ärinternalär tillgänglighetsdomänenMi skärningspunkten för tillgänglighetsdomänenTför med programtextenPi .
- Om den deklarerade tillgängligheten Mför ärprivateär tillgänglighetsdomänenMför programtextenTi .
Obs! Av dessa definitioner följer att tillgänglighetsdomänen för en kapslad medlem alltid är minst programtexten av den typ som medlemmen deklareras i. Dessutom följer att tillgänglighetsdomänen för en medlem aldrig är mer inkluderande än tillgänglighetsdomänen för den typ där medlemmen deklareras. slutkommentar
Obs! När en typ eller medlem
Manvänds intuitivt utvärderas följande steg för att säkerställa att åtkomsten tillåts:
MOm deklareras inom en typ (till skillnad från en kompileringsenhet eller ett namnområde) uppstår ett kompileringsfel om den typen inte är tillgänglig.
MOm ärpublictillåts åtkomsten sedan.- I annat fall, om
Märprotected internal, tillåts åtkomsten om den sker inom det program därMden deklareras, eller om den sker inom en klass som härleds från klassen därMdeklareras och sker via den härledda klasstypen (§7.5.4).- I annat fall, om
Märprotected, tillåts åtkomsten om den sker inom den klass därMden deklareras, eller om den sker inom en klass som härleds från den klass därMdeklareras och sker via den härledda klasstypen (§7.5.4).- I annat fall, om
Märinternal, tillåts åtkomsten om den inträffar i programmet därMdeklareras.- Om
Märprivatetillåts annars åtkomsten om den inträffar inom den typ somMdeklareras.- Annars är typen eller medlemmen otillgänglig och ett kompileringsfel inträffar. slutkommentar
Exempel: I följande kod
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; } }klasserna och medlemmarna har följande tillgänglighetsdomäner:
- Tillgänglighetsdomänen
Aför ochA.Xär obegränsad.- Tillgänglighetsdomänen
A.Yför ,B,B.X,B.Y,B.C,B.C.XochB.C.Yär programtexten för det innehållande programmet.- Tillgänglighetsdomänen
A.Zför är programtextenAi .- Tillgänglighetsdomänen för och
B.Zär programtextenB.Di , inklusive programtextenBför ochB.C.B.D- Tillgänglighetsdomänen
B.C.Zför är programtextenB.Ci .- Tillgänglighetsdomänen för och
B.D.Xär programtextenB.D.Yi , inklusive programtextenBför ochB.C.B.D- Tillgänglighetsdomänen
B.D.Zför är programtextenB.Di . Som exemplet visar är tillgänglighetsdomänen för en medlem aldrig större än för en innehållande typ. Till exempel, även om allaXmedlemmar har offentligt deklarerad tillgänglighet, har alla utomA.Xtillgänglighetsdomäner som begränsas av en innehållande typ.slutexempel
Enligt beskrivningen i §7.4 ärvs alla medlemmar i en basklass, förutom instanskonstruktorer, finalizers och statiska konstruktorer, av härledda typer. Detta inkluderar även privata medlemmar i en basklass. Tillgänglighetsdomänen för en privat medlem innehåller dock endast programtexten för den typ där medlemmen deklareras.
Exempel: I följande kod
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 } }klassen
Bärver den privata medlemmenxfrånAklassen. Eftersom medlemmen är privat är den endast tillgänglig i class_body avA. Därmed lyckas åtkomsten tillb.xmetodenA.F, men misslyckas iB.F-metoden.slutexempel
7.5.4 Skyddad åtkomst
När en protectedprivate protected instansmedlem används utanför programtexten för klassen där den deklareras och när en protected internal instansmedlem nås utanför programtexten i programmet där den deklareras, ska åtkomsten ske inom en klassdeklaration som härleds från klassen där den deklareras. Dessutom måste åtkomsten ske via en instans av den härledda klasstypen eller en klasstyp som skapats från den. Den här begränsningen hindrar en härledd klass från att komma åt skyddade medlemmar i andra härledda klasser, även när medlemmarna ärvs från samma basklass. Instansgränssnittsmedlemmar som definierats med protected eller private protected åtkomst kan inte nås från en class eller struct som implementerar gränssnittet. Dessa kan endast nås från härledda gränssnitt. 
              class
              struct Men typer kan definiera åsidosatta protected instansmedlemmar som deklareras i ett gränssnitt som implementeras.
Låt oss B vara en basklass som deklarerar en skyddad instansmedlem Moch låter vara D en klass som härleds från B. I class_body  av Dkan åtkomsten till M ha något av följande formulär:
- En okvalificerad type_name eller primary_expression av formuläret M.
- En .
- En primary_expression av formuläret base.M.
- En
Utöver dessa former av åtkomst kan en härledd klass komma åt en skyddad instanskonstruktor för en basklass i en constructor_initializer (§15.11.2).
Exempel: I följande kod
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 } }inom
Aär det möjligt att komma åtxvia instanser av bådeAochB, eftersom åtkomsten i båda fallen sker via en instans avAeller en klass som härletts frånA. Inom är det dockBinte möjligt att komma åtxvia en instans avA, eftersomAinte härleds frånB.slutexempel
Exempel:
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"; } }Här tillåts de tre tilldelningar
xsom ska utföras eftersom de alla sker via instanser av klasstyper som konstruerats från den generiska typen.slutexempel
Obs! Tillgänglighetsdomänen (§7.5.3) för en skyddad medlem som deklareras i en generisk klass innehåller programtexten för alla klassdeklarationer som härleds från alla typer som skapats från den generiska klassen. I exemplet:
class C<T> { protected static T x; } class D : C<string> { static void Main() { C<int>.x = 5; } }referensen till
protectedmedlemC<int>.xiDär giltig även om klassenDhärleds frånC<string>. slutkommentar
7.5.5 Hjälpmedelsbegränsningar
Flera konstruktioner på C#-språket kräver att en typ är minst lika tillgänglig som en medlem eller en annan typ. En typ T sägs vara minst lika tillgänglig som en medlem eller typ M om tillgänglighetsdomänen T för är en superuppsättning av tillgänglighetsdomänen Mför . Med andra ord T , är minst lika tillgänglig som M om T är tillgänglig i alla sammanhang där M är tillgänglig.
Följande hjälpmedelsbegränsningar finns:
- Den direkta basklassen för en klasstyp ska vara minst lika tillgänglig som själva klasstypen.
- De explicita basgränssnitten av en gränssnittstyp ska vara minst lika tillgängliga som själva gränssnittstypen.
- Returtypen och parametertyperna för en ombudstyp ska vara minst lika tillgängliga som själva ombudstypen.
- En konstants typ ska vara minst lika tillgänglig som konstanten själv.
- Typen av fält ska vara minst lika tillgänglig som själva fältet.
- Returtypen och parametertyperna för en metod ska vara minst lika tillgängliga som själva metoden.
- Egendomens typ ska vara minst lika tillgänglig som själva fastigheten.
- En händelsetyp ska vara minst lika tillgänglig som själva händelsen.
- Typ- och parametertyperna för en indexerare ska vara minst lika tillgängliga som indexeraren själv.
- En operatörs returtyp och parametertyper ska vara minst lika tillgängliga som operatören själv.
- Parametertyperna för en instanskonstruktor ska vara minst lika tillgängliga som själva instanskonstruktorn.
- Ett gränssnitts- eller klasstypsvillkor för en typparameter ska vara minst lika tillgängligt som den medlem som deklarerar villkoret.
Exempel: I följande kod
class A {...} public class B: A {...}-
Bklassen resulterar i ett kompileringsfel eftersomAden inte är minst lika tillgänglig somB.slutexempel
Exempel: På samma sätt i följande kod
class A {...} public class B { A F() {...} internal A G() {...} public A H() {...} }metoden
Hi resulterar iBett kompileringsfel eftersom returtypenAinte är minst lika tillgänglig som metoden.slutexempel
7.6 Signaturer och överlagring
Metoder, instanskonstruktorer, indexerare och operatorer kännetecknas av sina signaturer:
- Signaturen för en metod består av namnet på metoden, antalet typparametrar och typ- och parameteröverföringsläget för var och en av dess parametrar, i den ordning som anges från vänster till höger. För dessa ändamål identifieras alla typparametrar för metoden som inträffar i typen av en parameter inte med dess namn, utan av dess ordningstalsposition i typparameterlistan för metoden. Signaturen för en metod inkluderar specifikt inte returtyp, parameternamn, typparameternamn, typparameterbegränsningar, paramsparametermodifierare ellerthisom parametrar krävs eller är valfria.
- Signaturen för en instanskonstruktor består av typ- och parameteröverföringsläget för var och en av dess parametrar, som beaktas i ordningen från vänster till höger. Signaturen för en instanskonstruktor innehåller inte den paramsmodifierare som kan anges för parametern höger mest, eller om parametrar krävs eller är valfria.
- Signaturen för en indexerare består av typen av var och en av dess parametrar, som beaktas i ordningen från vänster till höger. Signaturen för en indexerare inkluderar inte elementtypen, och den innehåller inte heller den paramsmodifierare som kan anges för den högra parametern, eller om parametrar krävs eller är valfria.
- Signaturen för en operator består av namnet på operatorn och typen av var och en av dess parametrar, som beaktas i ordningen från vänster till höger. Signaturen för en operator innehåller inte resultattypen.
- Signaturen för en konverteringsoperator består av källtypen och måltypen. Den implicita eller explicita klassificeringen av en konverteringsoperator ingår inte i signaturen.
- Två signaturer av samma medlemstyp (metod, instanskonstruktor, indexerare eller operator) anses vara samma signaturer om de har samma namn, antal typparametrar, antal parametrar och parameteröverföringslägen och en identitetskonvertering finns mellan typerna av motsvarande parametrar (§10.2.2).
Signaturer är den aktiverande mekanismen för överlagring av medlemmar i klasser, structs och gränssnitt:
- Överlagring av metoder gör det möjligt för en klass, struct eller ett gränssnitt att deklarera flera metoder med samma namn, förutsatt att deras signaturer är unika inom den klassen, struct eller gränssnittet.
- Överlagring av instanskonstruktorer tillåter en klass eller struct att deklarera flera instanskonstruktorer, förutsatt att deras signaturer är unika inom den klassen eller structen.
- Överlagring av indexerare tillåter att en klass, struct eller ett gränssnitt deklarerar flera indexerare, förutsatt att deras signaturer är unika inom den klassen, structen eller gränssnittet.
- Överlagring av operatorer tillåter en klass eller struct att deklarera flera operatorer med samma namn, förutsatt att deras signaturer är unika inom den klassen eller structen.
Även om in, outoch ref parametermodifierare anses vara en del av en signatur, kan medlemmar som deklarerats i en enda typ inte skilja sig åt i signaturen enbart efter in, outoch ref. Ett kompileringsfel uppstår om två medlemmar deklareras i samma typ med signaturer som skulle vara desamma om alla parametrar i båda metoderna med out eller in modifierare ändrades till ref modifierare. För andra syften med signaturmatchning (t.ex. döljande eller åsidosättande), in, out, och ref betraktas som en del av signaturen och matchar inte varandra.
Obs! Den här begränsningen är att C#-program enkelt ska kunna översättas till att köras på en plattform som inte ger ett sätt att definiera metoder som skiljer sig åt enbart i
in,outochref. slutkommentar
Typerna object och dynamic särskiljs inte vid jämförelse av signaturer. Därför tillåts inte medlemmar som deklareras i en enda typ vars signaturer skiljer sig åt genom att object ersätta med dynamic .
Exempel: I följande exempel visas en uppsättning överlagrade metoddeklarationer tillsammans med deras signaturer.
interface ITest { void F(); // F() void F(int x); // F(int) void F(ref int x); // F(ref int) 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 }Observera att alla
in,outochrefparametermodifierare (§15.6.2) ingår i en signatur.F(int)Därför är ,F(in int),F(out int)ochF(ref int)alla unika signaturer. Men ,F(in int)F(out int), ochF(ref int)kan inte deklareras i samma gränssnitt eftersom deras signaturer skiljer sig enbartinefter ,outochref. Observera också att returtypen ochparamsmodifieraren inte ingår i en signatur, så det går inte att överbelasta enbart baserat på returtyp eller på inkludering eller uteslutning avparamsmodifieraren. Därför resulterar deklarationerna av metodernaF(int)ochF(params string[])som identifieras ovan i ett kompileringsfel. slutexempel
7.7 Omfattningar
7.7.1 Allmänt
Omfånget för ett namn är den region i programtexten där det är möjligt att referera till den entitet som deklareras med namnet utan att namnet har kvalificerats. Omfång kan kapslas och ett inre omfång kan omdeklareras innebörden av ett namn från ett yttre omfång. (Detta tar dock inte bort den begränsning som föreskrivs i §7.3 att det inom ett kapslat block inte går att deklarera en lokal variabel eller lokal konstant med samma namn som en lokal variabel eller lokal konstant i ett omslutande block.) Namnet från det yttre omfånget sägs sedan vara dolt i området för programtext som omfattas av det inre omfånget, och åtkomst till det yttre namnet är endast möjligt genom att kvalificera namnet.
- Omfånget för en namnområdesmedlem som deklareras av en namespace_member_declaration (§14.6) utan att omsluta namespace_declaration är hela programtexten. 
- Omfånget för en namnområdesmedlem som deklareras av en namespace_member_declaration inom en namespace_declaration vars fullständigt kvalificerade namn är - N, är namespace_body för varje namespace_declaration vars fullständigt kvalificerade namn är- Neller börjar med- N, följt av en period.
- Omfånget för ett namn som definieras av en extern_alias_directive (§14.4) sträcker sig över using_directives, global_attributes och namespace_member_declarationav dess omedelbart innehållande compilation_unit eller namespace_body. En extern_alias_directive bidrar inte med några nya medlemmar till det underliggande deklarationsutrymmet. Med andra ord är en extern_alias_directive inte transitiv, utan påverkar snarare bara de compilation_unit eller namespace_body där den inträffar. 
- Omfånget för ett namn som definierats eller importerats av en using_directive (§14.5) sträcker sig över global_attributes och namespace_member_declarationi compilation_unit eller namespace_body där using_directive inträffar. En using_directive kan göra noll eller fler namnområden eller typnamn tillgängliga inom en viss compilation_unit eller namespace_body, men bidrar inte med några nya medlemmar till det underliggande deklarationsutrymmet. Med andra ord är en using_directive inte transitiv utan påverkar bara compilation_uniteller namespace_body där den inträffar. 
- Omfånget för en typparameter som deklareras av en type_parameter_list på en class_declaration (§15.2) är class_base, type_parameter_constraints_clauses och class_body för den class_declaration. - Obs! Till skillnad från medlemmar i en klass utökas inte det här omfånget till härledda klasser. slutkommentar 
- Omfånget för en typparameter som deklareras av en type_parameter_list på en struct_declaration (§16.2) är struct_interfaces, type_parameter_constraints_clauses och struct_body för den struct_declaration. 
- Omfånget för en typparameter som deklareras av en type_parameter_list på en interface_declaration (§19.2) är interface_base, type_parameter_constraints_clauses och interface_body för den interface_declaration. 
- Omfånget för en typparameter som deklareras av en type_parameter_list på en delegate_declaration (§21.2) är return_type, parameter_list och type_parameter_constraints_clauseav den delegate_declaration. 
- Omfånget för en typparameter som deklareras av en type_parameter_list på en method_declaration (§15.6.1) är method_declaration. 
- Omfånget för en medlem som deklareras av en class_member_declaration (§15.3.1) är den class_body i vilken deklarationen sker. Dessutom omfattar omfattningen för en klassmedlem class_body av de härledda klasserna som ingår i medlemmens tillgänglighetsdomän (§7.5.3). 
- Omfånget för en medlem som deklareras av en struct_member_declaration (§16.3) är den struct_body i vilken deklarationen sker. 
- Omfånget för en medlem som deklareras av en enum_member_declaration (§20.4) är den enum_body i vilken deklarationen sker. 
- Omfånget för en parameter som deklareras i en method_declaration (§15.6) är method_bodyeller ref_method_body för den method_declaration. 
- Omfånget för en parameter som deklareras i en indexer_declaration (§15.9) är indexer_bodyför den indexer_declaration. 
- Omfånget för en parameter som deklareras i en operator_declaration (§15.10) är operator_body för den operator_declaration. 
- Omfånget för en parameter som deklareras i en constructor_declaration (§15.11) är constructor_initializer och blocket för den constructor_declaration. 
- Omfånget för en parameter som deklareras i en lambda_expression (§12.20) är lambda_expression_body för den lambda_expression. 
- Omfånget för en parameter som deklareras i en anonymous_method_expression (§12.20) är blocket för den anonymous_method_expression. 
- Omfånget för en etikett som deklareras i en labeled_statement (§13.5) är det block där deklarationen inträffar. 
- Omfånget för en lokal variabel som deklareras i en local_variable_declaration (§13.6.2) är det block där deklarationen sker. 
- Omfånget för en lokal variabel som deklareras i en switch_block av en - switchinstruktion (§13.8.3) är switch_block.
- Omfånget för en lokal variabel som deklareras i en for_initializer av en - forinstruktion (§13.9.4) är for_initializer, for_condition, for_iterator och embedded_statement av -instruktionen- for.
- Omfånget för en lokal konstant som deklareras i en local_constant_declaration (§13.6.3) är det block där deklarationen inträffar. Det är ett kompileringsfel att referera till en lokal konstant i en textposition som föregår dess constant_declarator. 
- Omfånget för en variabel som deklareras som en del av en foreach_statement, using_statement, lock_statement eller query_expression bestäms av expansionen av den angivna konstruktionen. 
Inom omfånget för en namnrymd, klass, struct eller uppräkningsmedlem är det möjligt att referera till medlemmen i en textposition som föregår medlemmens deklaration.
Exempel:
class A { void F() { i = 1; } int i = 0; }Här är det giltigt för
Fatt referera tilliinnan det deklareras.slutexempel
Inom omfånget för en lokal variabel är det ett kompileringsfel att referera till den lokala variabeln i en textposition som föregår dess deklarator.
Exempel:
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 } }
FI metoden ovan refererar den första tilldelningen tillispecifikt inte till det fält som deklarerats i det yttre omfånget. I stället refererar den till den lokala variabeln och resulterar i ett kompileringsfel eftersom den textmässigt föregår deklarationen av variabeln.GI metoden är användningen avji initiatorn för deklarationen avjgiltig eftersom användningen inte föregår deklaratorn.HI metoden refererar en efterföljande deklarator korrekt till en lokal variabel som deklarerats i en tidigare deklarator inom samma local_variable_declaration.slutexempel
Obs! Omfångsreglerna för lokala variabler och lokala konstanter är utformade för att garantera att innebörden av ett namn som används i en uttryckskontext alltid är densamma inom ett block. Om omfånget för en lokal variabel endast skulle utökas från deklarationen till slutet av blocket, skulle den första tilldelningen i exemplet ovan tilldela till instansvariabeln och den andra tilldelningen skulle tilldela till den lokala variabeln, vilket kan leda till kompileringsfel om blockinstruktionerna senare skulle ordnas om.)
Innebörden av ett namn i ett block kan variera beroende på i vilken kontext namnet används. I exemplet
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" } }namnet
Aanvänds i en uttryckskontext för att referera till den lokala variabelnAoch i en typkontext för att referera till klassenA.slutkommentar
7.7.2 Namn som döljs
7.7.2.1 Allmänt
Omfånget för en entitet omfattar vanligtvis mer programtext än entitetens deklarationsutrymme. I synnerhet kan en entitets omfattning innehålla deklarationer som introducerar nya deklarationsutrymmen som innehåller entiteter med samma namn. Sådana deklarationer gör att den ursprungliga entiteten döljs. Omvänt sägs en entitet vara synlig när den inte är dold.
Namn som döljs uppstår när omfång överlappar genom kapsling och när omfång överlappar genom arv. Egenskaperna för de två typerna av döljande beskrivs i följande underetiketter.
7.7.2.2 Dölja genom kapsling
Namn som döljs genom kapsling kan inträffa som ett resultat av kapsling av namnområden eller typer i namnområden, till följd av kapslingstyper inom klasser eller structs, som ett resultat av en lokal funktion eller en lambda, och som ett resultat av parameter, lokal variabel och lokala konstanta deklarationer.
Exempel: I följande kod
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; } }
Fi metoden döljs instansvariabelniav den lokala variabelni, men inomGmetodenirefererar den fortfarande till instansvariabeln. Inuti den lokala funktionenM1float idöljer den omedelbara yttrei. Lambda-parameternidöljerfloat iinsidan av lambdakroppen.slutexempel
När ett namn i ett inre omfång döljer ett namn i ett yttre omfång döljer det alla överlagrade förekomster av det namnet.
Exempel: I följande kod
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 } } }anropet
F(1)anropar denFdeklarerade iInnereftersom alla yttre förekomster avFär dolda av den inre deklarationen. Av samma anledning resulterar anropetF("Hello")i ett kompileringsfel.slutexempel
7.7.2.3 Gömma sig genom arv
Namn som döljs genom arv inträffar när klasser eller structs omdeklarerat namn som ärvts från basklasser. Den här typen av namn som döljs har något av följande formulär:
- En konstant, fält, egenskap, händelse eller typ som introduceras i en klass, struct eller ett gränssnitt döljer alla basklassmedlemmar med samma namn.
- En metod som introduceras i en klass, struct eller ett gränssnitt döljer alla icke-metodbaserade basklassmedlemmar med samma namn och alla basklassmetoder med samma signatur (§7.6).
- En indexerare som introduceras i en klass, struct eller ett gränssnitt döljer alla indexerare av bastyp med samma signatur (§7.6) .
Reglerna för operatordeklarationer (§15.10) gör det omöjligt för en härledd klass att deklarera en operator med samma signatur som en operatör i en basklass. Därför döljer operatorerna aldrig varandra.
I motsats till att dölja ett namn från ett yttre omfång, orsakar döljande av ett synligt namn från ett ärvt omfång en varning rapporteras.
Exempel: I följande kod
class Base { public void F() {} } class Derived : Base { public void F() {} // Warning, hiding an inherited name }deklarationen av
FiDerivedgör att en varning rapporteras. Att dölja ett ärvt namn är specifikt inte ett fel, eftersom det skulle utesluta en separat utveckling av basklasser. Ovanstående situation kan till exempel ha uppkommit eftersom en senare version avBaseintroducerade enFmetod som inte fanns i en tidigare version av klassen.slutexempel
Varningen som orsakas av att ett ärvt namn döljs kan elimineras med hjälp av new modifieraren:
Exempel:
class Base { public void F() {} } class Derived : Base { public new void F() {} }Modifieraren
newanger attFinDerivedär "ny" och att den verkligen är avsedd att dölja den ärvda medlemmen.slutexempel
En deklaration av en ny medlem döljer endast en ärvd medlem inom den nya medlemmens omfång.
Exempel:
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 } }I exemplet ovan döljer indeklarationen som ärvts från
F, men eftersom den nyaDerivedinFhar privat åtkomst utökas inte dess omfång tillBase.FDerivedMoreDerivedDärför är anropetF()iMoreDerived.Ggiltigt och anroparBase.F.slutexempel
7.8 Namnområde och typnamn
7.8.1 Allmänt
Flera kontexter i ett C#-program kräver att en namespace_name eller en type_name anges.
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?)*
    ;
En namespace_name är en namespace_or_type_name som refererar till ett namnområde.
Efter en lösning enligt beskrivningen nedan ska namespace_or_type_name för en namespace_name referera till ett namnområde, eller på annat sätt uppstår ett kompileringstidsfel. Inga typargument (§8.4.2) kan finnas i en namespace_name (endast typer kan ha typargument).
En type_name är en namespace_or_type_name som refererar till en typ.
Efter en lösning enligt beskrivningen nedan ska namespace_or_type_name av en type_name referera till en typ, eller på annat sätt uppstår ett kompileringsfel.
En namespace_or_type_name refererar till en typ eller ett namnområde. Lösning på ett visst namnområde eller en viss typ omfattar två steg baserat på att dela upp grammatiken i en inledande del, vilket är ett av grammatikfragmenten:
- identifier type_argument_list?
- qualified_alias_member
och en avslutande del, som är grammatikfragmentet:
- ('.' identifier type_argument_list?)*
Först matchas den inledande delen för att fastställa R₀, startnamnområdet eller typen.
Om den inledande delen av namespace_or_type_name är en qualified_alias_member, då är det R₀ det namnområde eller den typ som identifieras genom att lösa det enligt beskrivningen i §14.8.1.
Annars kommer den inledande delen, som är identifieraren för grammatikfragmentet type_argument_list?, att ha något av formulären:
- I
- I<A₁, ..., Aₓ>
där:
- 
              Iär en enda identifierare. och
- 
              <A₁, ..., Aₓ>är en type_argument_list, och när ingen type_argument_list anges, betraktasxsom noll.
              R₀ bestäms på följande sätt:
- Om xär noll och namespace_or_type_name visas inom en allmän metoddeklaration (§15.6) men utanför attributen för dess metodhuvud, och om den deklarationen innehåller en typparameter (§15.2.3) med namnetI, refererar duR₀till den typparametern.
- Annars, om namespace_or_type_name visas inom en typdeklaration, så för varje instanstyp T(§15.3.2), börjar med instanstypen för den typdeklarationen och fortsätter med instanstypen för varje omslutande klass, struct eller gränssnittsdeklaration (om någon):- Om xär noll och deklarationen avTinnehåller en typparameter med namnetIrefererar duR₀till den typparametern.
- Annars, om namespace_or_type_name visas i brödtexten i typdeklarationen, och Teller någon av dess bastyper innehåller en kapslad tillgänglig typ med namnIochxtypparametrar, refererar denR₀typen som skapats med de angivna typargumenten. Om det finns fler än en sådan typ väljs den typ som deklarerats inom den mer härledda typen. Det är ett kompilatorfel om ingen typ är mer härledd än alla andra.Obs! Icke-typmedlemmar (konstanter, fält, metoder, egenskaper, indexerare, operatorer, instanskonstruktorer, finalizers och statiska konstruktorer) och typmedlemmar med ett annat antal typparametrar ignoreras när innebörden av namespace_or_type_name fastställs. slutkommentar 
- I annat fall utvärderas följande steg tills en entitet finns för varje namnområde Nsom börjar med namnområdet där namespace_or_type_name inträffar, fortsätter med varje omslutande namnområde (om det finns) och slutar med det globala namnområdet:- Om xär noll ochIär namnet på ett namnområde iN, så:- Om platsen där namespace_or_type_name inträffar omges av en namnområdesdeklaration för Noch namnområdesdeklarationen innehåller en extern_alias_directive eller using_alias_directive som associerar namnetImed ett namnområde eller typ, är namespace_or_type_name tvetydig och ett kompileringsfel inträffar.
- Annars R₀refererar till namnområdet med namnetIiN.
 
- Om platsen där namespace_or_type_name inträffar omges av en namnområdesdeklaration för 
- Annars, om Ninnehåller en tillgänglig typ med namnIochxtypparametrar, så:- Om xär noll och platsen där namespace_or_type_name inträffar omges av en namnområdesdeklaration förNoch namnområdesdeklarationen innehåller en extern_alias_directive eller using_alias_directive som associerar namnetImed ett namnområde eller typ, är namespace_or_type_name tvetydig och ett kompileringsfel inträffar.
- I annat fall R₀refererar till den typ som skapats med de angivna typargumenten.
 
- Om 
- Annars om platsen där namespace_or_type_name  inträffar omges av en namnområdesdeklaration för N:- Om xär noll och namnområdesdeklarationen innehåller en extern_alias_directive eller using_alias_directive som associerar namnetImed ett importerat namnområde eller typ refererar duR₀till namnområdet eller typen.
- Annars, om namnrymderna som importeras av using_namespace_directivei namnområdesdeklarationen innehåller exakt en typ med namn Iochxtypparametrar, refererar tillR₀den typen som skapats med de angivna typargumenten.
- Annars, om namnrymderna som importeras av using_namespace_directive i namnområdesdeklarationen innehåller mer än en typ som har namnet Iochxsom typparametrar, är namespace_or_type_name tvetydig och ett kompileringsfel inträffar.
 
- Om 
 
- Om 
- Annars är namespace_or_type_name odefinierat och ett kompileringsfel inträffar.
 
- Om 
Om R₀ har lösts löses den avslutande delen av namespace_or_type_name . Det avslutande grammatikfragmentet består av k ≥ 0 upprepningar, där varje upprepning ytterligare löser det refererade namnområdet eller typen.
Om k är noll, dvs. det inte finns någon avslutande del, matchas namespace_or_type_name till R₀.
Annars har varje upprepning ett av formulären:
- .I
- .I<A₁, ..., Aₓ>
där I, A och x definieras som ovan.
För varje upprepning n, där 1 ≤ n ≤ k, dess upplösning, Rₙ; som omfattar Rₚ, där p = n - 1, upplösningen av den föregående upprepningen, bestäms på följande sätt:
- Om xär noll ochRₚrefererar till ett namnområde ochRₚinnehåller ett kapslat namnområde med namnetIrefererar duRₙtill det kapslade namnområdet.
- Annars, om Rₚrefererar till ett namnområde ochRₚinnehåller en tillgänglig typ med namnI- ochxtypparametrar, refererar duRₙtill den typen som skapats med de angivna typargumenten.
- Annars, om Rₚrefererar till en (möjligen konstruerad) klass, struct eller gränssnittstyp ochRₚeller någon av dess bastyper innehåller en kapslad tillgänglig typ med namnIochxtypparametrar, refererar denRₙtypen konstruerad med de angivna typargumenten. Om det finns fler än en sådan typ väljs den typ som deklarerats inom den mer härledda typen. Det är ett kompilatorfel om ingen typ är mer härledd än alla andra.Obs! Om innebörden av T.I, för någon typT, bestäms som en del av lösningen avTbasklassspecifikationen för anses den direkta basklassenTför varaobject(§15.2.4.2). slutkommentar
- Annars är namespace_or_type_name ogiltig och ett kompileringsfel inträffar.
Lösningen av namespace_or_type_name är lösningen på den slutliga upprepningen, Rₖ.
En namespace_or_type_name får endast referera till en statisk klass (§15.2.2.4) om
- Namespace_or_type_name  är Ti ett namespace_or_type_name i formuläretT.I, eller
- 
              namespace_or_type_name är Ti en typeof_expression (§12.8.18) av formulärettypeof(T)
Exempel:
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 }I exemplet ovan är anropet till
NestedClass.M()iC.Test()tvetydigt mellanB.NestedClass.M()ochA.NestedClass.M()eftersom ingen av dem är mer härledd än den andra. En explicit referens till antingenA.NestedClass.M()ellerB.NestedClass.M()krävs för att lösa tvetydigheten.slutexempel
7.8.2 Okvalificerade namn
Varje namnområdesdeklaration och typdeklaration har ett okvalificerat namn som fastställs på följande sätt:
- För en namnområdesdeklaration är det okvalificerade namnet den qualified_identifier som anges i deklarationen.
- För en typdeklaration utan type_parameter_list är det okvalificerade namnet den identifierare som anges i deklarationen.
- För en typdeklaration med K-typparametrar är det okvalificerade namnet den identifierare som anges i deklarationen, följt av generisk dimensionsspecificerare (§12.8.18) för K-typparametrar.
7.8.3 Fullständigt kvalificerade namn
Varje namnområde och typdeklaration har ett fullständigt kvalificerat namn som unikt identifierar namnområdet eller typdeklarationen bland alla andra i programmet. Det fullständigt kvalificerade namnet på ett namnområde eller en typdeklaration med okvalificerat namn N bestäms på följande sätt:
- Om När medlem i det globala namnområdet ärNdess fullständigt kvalificerade namn .
- Annars är S.Ndess fullständigt kvalificerade namn , därSär det fullständigt kvalificerade namnet på namnområdet eller typdeklarationen somNdeklareras i.
Med andra ord är det fullständigt kvalificerade namnet N på den fullständiga hierarkiska sökvägen för identifierare och generic_dimension_specifiersom leder till N, med början från det globala namnområdet. Eftersom varje medlem i ett namnområde eller en typ ska ha ett unikt namn, följer det att det fullständigt kvalificerade namnet på ett namnområde eller typdeklaration alltid är unikt. Det är ett kompileringsfel för samma fullständigt kvalificerade namn för att referera till två distinkta entiteter. Framför allt:
- Det är ett fel för både en namnområdesdeklaration och en typdeklaration att ha samma fullständigt kvalificerade namn.
- Det är ett fel att två olika typer av typdeklarationer har samma fullständigt kvalificerade namn (till exempel om både en struct- och klassdeklaration har samma fullständigt kvalificerade namn).
- Det är ett fel för en typdeklaration utan den partiella modifieraren att ha samma fullständigt kvalificerade namn som en annan typdeklaration (§15.2.7).
Exempel: Exemplet nedan visar flera namnområden och typdeklarationer tillsammans med deras associerade fullständigt kvalificerade namn.
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<> } }slutexempel
7.9 Automatisk minneshantering
C# använder automatisk minneshantering, vilket frigör utvecklare från att manuellt allokera och frigöra minne som upptas av objekt. Principer för automatisk minneshantering implementeras av en skräpinsamlare. Livscykeln för minneshantering för ett objekt är följande:
- När objektet skapas allokeras minne för det, konstruktorn körs och objektet betraktas som live.
- Om varken objektet eller något av dess instansfält kan nås genom någon eventuell fortsättning av körningen, förutom körningen av finalizers, anses objektet inte längre vara i bruk och det blir berättigat till slutförande.
Obs! C#-kompilatorn och skräpinsamlaren kan välja att analysera kod för att avgöra vilka referenser till ett objekt som kan användas i framtiden. Om till exempel en lokal variabel som finns i omfånget är den enda befintliga referensen till ett objekt, men den lokala variabeln aldrig refereras till i någon eventuell fortsättning av körningen från den aktuella körningspunkten i proceduren, kan skräpinsamlaren (men krävs inte) behandla objektet som inte längre används. slutkommentar 
- När objektet är berättigat till slutförande körs slutaktivatorn (§15.13) (om det finns någon) för objektet vid något ospecificerat senare tillfälle. Under normala omständigheter körs slutföraren för objektet endast en gång, även om implementeringsdefinierade API:er kan tillåta att det här beteendet åsidosätts.
- När slutföraren för ett objekt har körts, om varken objektet eller något av dess instansfält kan nås genom någon eventuell fortsättning av körningen, inklusive körning av finalizers, anses objektet vara otillgängligt och objektet blir berättigat till samling.
Obs! Ett objekt som tidigare inte kunde nås kan bli tillgängligt igen på grund av dess finalizer. Ett exempel på detta finns nedan. slutkommentar 
- När objektet har kvalificerats för insamling frigör skräpinsamlaren slutligen det minne som är associerat med objektet.
Skräpinsamlaren underhåller information om objektanvändning och använder den här informationen för att fatta beslut om minneshantering, till exempel var i minnet ett nyligen skapat objekt ska hittas, när ett objekt ska flyttas och när ett objekt inte längre används eller är otillgängligt.
Precis som andra språk som förutsätter att det finns en skräpinsamlare är C# utformat så att skräpinsamlaren kan implementera en mängd olika principer för minneshantering. C# anger varken en tidsbegränsning inom det intervallet eller en ordning i vilken finalizers körs. Huruvida slutförare körs som en del av programavslut är implementeringsdefinierat (§7.2).
Beteendet för skräpinsamlaren kan till viss del styras via statiska metoder i klassen System.GC. Den här klassen kan användas för att begära att en samling ska ske, slutförare som ska köras (eller inte köras) och så vidare.
Exempel: Eftersom skräpinsamlaren tillåts bred latitud för att bestämma när objekt ska samlas in och köra finalizers kan en implementering som överensstämmer generera utdata som skiljer sig från den som visas i följande kod. Programmet
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(); } }skapar en instans av klassen
Aoch en instans av klassenB. Dessa objekt blir berättigade till skräpinsamling när variabelnbtilldelas värdetnull, eftersom det efter den här tiden är omöjligt för någon användarskriven kod att komma åt dem. Utdata kan vara antingenFinalize instance of A Finalize instance of Beller
Finalize instance of B Finalize instance of Aeftersom språket inte medför några begränsningar för i vilken ordning objekt samlas in.
I subtila fall kan skillnaden mellan "berättigande till slutförande" och "berättigad till insamling" vara viktig. Ett exempel:
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"); } } }Om skräpinsamlaren i programmet ovan väljer att köra slutversionen av
Aföre slutföraren avBkan utdata från det här programmet vara:Finalize instance of A Finalize instance of B A.F RefA is not nullObservera att även om instansen av
Ainte användes ochA"s finalizer kördes, är det fortfarande möjligt att metoderAför (i det här falletF) anropas från en annan finalator. Observera också att körningen av en finalator kan leda till att ett objekt kan användas från huvudlinjeprogrammet igen. I det här fallet orsakade körningen avB's finalizer en instans avAsom tidigare inte användes, att bli tillgänglig från livereferensenTest.RefA. Efter anropet till är instansen av berättigad tillWaitForPendingFinalizerssamling, men instansen avBär inte det på grund av referensenA.Test.RefAslutexempel
7.10 Körningsordning
Körningen av ett C#-program fortsätter så att biverkningarna av varje körningstråd bevaras vid kritiska körningsplatser. En bieffekt definieras som en läsning eller skrivning av ett flyktigt fält, en skrivning till en icke-flyktig variabel, en skrivning till en extern resurs och genererar ett undantag. De kritiska körningspunkter där ordningen på dessa biverkningar ska bevaras är referenser till flyktiga fält (§15.5.4), lock instruktioner (§13.13) och trådskapande och avslutning. Körningsmiljön kan ändra körningsordningen för ett C#-program, med följande begränsningar:
- Databeroende bevaras i en körningstråd. Värdet för varje variabel beräknas som om alla instruktioner i tråden kördes i ursprunglig programordning.
- Regler för initieringsbeställning bevaras (§15.5.5, §15.5.6).
- Ordningen på biverkningar bevaras med avseende på flyktiga läsningar och skrivningar (§15.5.4). Dessutom behöver körningsmiljön inte utvärdera en del av ett uttryck om den kan dra slutsatsen att uttryckets värde inte används och att inga nödvändiga biverkningar skapas (inklusive eventuella som orsakas av att anropa en metod eller komma åt ett flyktigt fält). När programkörningen avbryts av en asynkron händelse (till exempel ett undantag som utlöses av en annan tråd) är det inte säkert att de observerbara biverkningarna visas i den ursprungliga programordningen.
ECMA C# draft specification