Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
7.1 Spuštění aplikace
Program může být zkompilován buď jako knihovna tříd, která se má použít jako součást jiných aplikací, nebo jako aplikace , která může být spuštěna přímo. Mechanismus pro určení tohoto režimu kompilace je definován implementací a mimo tuto specifikaci.
Program zkompilovaný jako aplikace musí obsahovat alespoň jednu metodu, která je kvalifikující jako vstupní bod, a to splněním následujících požadavků:
- Musí mít název
Main. - Musí být
static. - Nesmí být obecný.
- Deklaruje se v ne generickém typu. Pokud je typ deklarující metodu vnořeným typem, nemusí být žádný z jeho uzavřených typů obecný.
- Může mít modifikátor za předpokladu
async, že návratový typ metody jeSystem.Threading.Tasks.TaskneboSystem.Threading.Tasks.Task<int>. - Návratový typ musí být
void,int,System.Threading.Tasks.TaskneboSystem.Threading.Tasks.Task<int>. - Nejedná se o částečnou metodu (§15.6.9) bez provedení.
- Seznam parametrů musí být prázdný nebo musí mít jeden parametr hodnoty typu
string[].
Poznámka: Metody s modifikátorem
asyncmusí mít přesně jeden ze dvou návratových typů uvedených výše, aby bylo možné kvalifikovat jako vstupní bod. Metodaasync voidneboasyncmetoda vracející jiný typ awaitable, napříkladValueTaskneboValueTask<int>se neopravuje jako vstupní bod. koncová poznámka
Pokud je v rámci programu deklarováno více než jedna metoda, která se považuje za skutečný vstupní bod aplikace, lze použít externí mechanismus k určení metody, která se považuje za skutečný vstupní bod aplikace. Pokud je kvalifikující metoda s návratovým typem int nebo void nalezena, jakákoli kvalifikující metoda s návratovým typem System.Threading.Tasks.Task nebo System.Threading.Tasks.Task<int> se nepovažuje za metodu vstupního bodu. Jedná se o chybu v době kompilace pro program, který se má zkompilovat jako aplikace bez přesně jednoho vstupního bodu. Program zkompilovaný jako knihovna tříd může obsahovat metody, které by se kvalifikovaly jako vstupní body aplikace, ale výsledná knihovna nemá žádný vstupní bod.
Obvykle je deklarovaná přístupnost (§7.5.2) metody určena modifikátory přístupu (§15.3.6) specifikovanými v deklaraci a podobně deklarovaná přístupnost typu je určena modifikátory přístupu specifikovanými v prohlášení. Aby byla daná metoda daného typu volatelná, musí být typ i člen přístupné. Vstupní bod aplikace je však zvláštní případ. Konkrétně může spouštěcí prostředí přistupovat k vstupnímu bodu aplikace bez ohledu na jeho deklarovanou přístupnost a bez ohledu na deklarovanou přístupnost u svých uzavřených deklarací typu.
Pokud má metoda vstupního bodu návratový typ System.Threading.Tasks.Task nebo System.Threading.Tasks.Task<int>, kompilátor syntetizuje synchronní metodu vstupního bodu, která volá odpovídající metodu Main. Syntetizovaná metoda má parametry a návratové typy založené na Main metodě:
- Seznam parametrů syntetizované metody je stejný jako seznam
Mainparametrů metody. - Pokud je
MainnávratovýSystem.Threading.Tasks.Tasktyp metody , návratový typ syntetizované metody jevoid - Pokud je
MainnávratovýSystem.Threading.Tasks.Task<int>typ metody , návratový typ syntetizované metody jeint
Provedení syntetizované metody pokračuje následujícím způsobem:
- Syntetizovaná metoda volá metodu
Maina předává jejístring[]hodnotu parametru jako argument,Mainpokud metoda má takový parametr. - Pokud metoda
Mainvyvolá výjimku, výjimka se rozšíří syntetizovanou metodou. - Jinak syntetizovaný vstupní bod čeká na dokončení vráceného úkolu, volání
GetAwaiter().GetResult()úlohy pomocí metody instance bez parametrů nebo metody rozšíření popsané v §C.3. Pokud úloha selže,GetResult()vyvolá výjimku a tato výjimka se rozšíří syntetizovanou metodou. - Pro metodu
Mains návratovým typemSystem.Threading.Tasks.Task<int>, pokud úloha úspěšně dokončí,inthodnota vrácenáGetResult()syntetizovanou metodou.
Efektivní vstupní bod aplikace je vstupní bod deklarovaný v rámci programu nebo syntetizovaná metoda, pokud je požadována, jak je popsáno výše. Návratový typ platného vstupního bodu je proto vždy void nebo int.
Při spuštění aplikace se vytvoří nová doména aplikace. Na stejném počítači může existovat několik různých instancí aplikace a každá má vlastní doménu aplikace. Doména aplikace umožňuje izolaci aplikací tak, že funguje jako kontejner pro stav aplikace. Doména aplikace funguje jako kontejner a hranice pro typy definované v aplikaci a knihovny tříd, které používá. Typy načtené do jedné domény aplikace se liší od stejných typů načtených do jiné domény aplikace a instance objektů se mezi doménami aplikace nesdílí přímo. Každá doména aplikace má například vlastní kopii statických proměnných pro tyto typy a statický konstruktor pro typ se spouští maximálně jednou pro každou doménu aplikace. Implementace mohou poskytovat zásady nebo mechanismy definované implementací pro vytváření a zničení domén aplikací.
K spuštění aplikace dojde, když spouštěcí prostředí volá efektivní vstupní bod aplikace. Pokud efektivní vstupní bod deklaruje parametr, pak při spuštění aplikace zajistí implementace, že počáteční hodnota tohoto parametru je nenulový odkaz na řetězcové pole. Toto pole se skládá z nenulových odkazů na řetězce, označované jako parametry aplikace, které mají před spuštěním aplikace definované hodnoty definované implementací hostitelského prostředí. Záměrem je poskytnout informace o aplikaci určené před spuštěním aplikace z jiného prostředí v hostovaném prostředí.
Poznámka: V systémech podporujících příkazový řádek odpovídají parametry aplikace obecně známým jako argumenty příkazového řádku. koncová poznámka
Je-li návratový typ intplatného vstupního bodu, použije se při ukončení aplikace návratová hodnota z metody vyvolání spouštěcím prostředím (§7.2).
Kromě výše uvedených situací se metody vstupních bodů chovají jako metody, které nejsou vstupními body v každém ohledu. Konkrétně platí, že pokud je vstupní bod vyvolán v jiném bodě během životnosti aplikace, jako je například běžné vyvolání metody, neexistuje žádné zvláštní zpracování metody: pokud existuje parametr, může mít počáteční hodnotu nullnebo jinounull hodnotu odkazující na matici, která obsahuje odkazy na hodnotu null. Stejně tak návratová hodnota vstupního bodu nemá žádný zvláštní význam než při vyvolání z spouštěcího prostředí.
7.2 Ukončení aplikace
Návrat řízení do spouštěcího prostředí se označuje jako ukončení aplikace.
Pokud je návratový typ efektivní metody int vstupního bodu aplikace a provádění se dokončí, aniž by došlo k výjimce, hodnota int vrácených slouží jako stavový kód ukončení aplikace. Účelem tohoto kódu je umožnit komunikaci úspěšného nebo neúspěšného spuštění prostředí. Pokud je návratový typ efektivní metody void vstupního bodu a provádění se dokončí bez výsledku výjimky, stavový kód ukončení je 0.
Pokud účinná metoda vstupního bodu skončí z důvodu výjimky (§22.4), je výstupní kód definován implementací. Kromě toho může implementace poskytovat alternativní rozhraní API pro zadání ukončovacího kódu.
Zda jsou finalizátory (§15.13) spuštěny jako součást ukončení aplikace, je definována implementace.
Poznámka: Implementace rozhraní .NET Framework provádí veškeré přiměřené úsilí při volání finalizátorů (§15.13) pro všechny objekty, které dosud nebyly uvolněny z paměti, pokud takové vyčištění nebylo potlačeno (například voláním metody
GC.SuppressFinalizeknihovny). koncová poznámka
7.3 Prohlášení
Deklarace v programu jazyka C# definují základní prvky programu. Programy v jazyce C# jsou uspořádány pomocí oborů názvů. Tyto deklarace se zavádějí pomocí deklarací oboru názvů (§14), které mohou obsahovat deklarace typů a vnořené deklarace oboru názvů. Prohlášení o typu (§14.7) slouží k definování tříd (§15), struktur (§16), rozhraní (§19), výčtů (§20) a delegátů (§21). Typy členů povolených v deklaraci typu závisí na podobě deklarace typu. Například: Deklarace třídy mohou obsahovat deklarace konstant (§15.4), pole (§15.5), metody (§15.6), vlastnosti (§15.7), události (§15.8), indexery (§15.9) ), operátory (§15.10), konstruktory instancí (§15.11), statické konstruktory (§15.12), finalizátory (§15.13) a vnořené typy (§15.3.9).
Deklarace definuje název v prostoru deklarace, do kterého deklarace patří. Jedná se o chybu v době kompilace, která obsahuje dvě nebo více deklarací, které zavádějí členy se stejným názvem v prostoru deklarací s výjimkou následujících případů:
- Dvě nebo více deklarací oboru názvů se stejným názvem jsou povoleny ve stejném prostoru deklarace. Tyto deklarace oboru názvů jsou agregovány tak, aby vytvořily jeden logický obor názvů a sdílely jediný prostor deklarace.
- Deklarace v samostatných programech, ale ve stejném prostoru deklarace oboru názvů mohou sdílet stejný název.
Poznámka: Tyto deklarace však mohou v případě zahrnutí do stejné aplikace zavést nejednoznačnosti. koncová poznámka
- Dvě nebo více metod se stejným názvem, ale odlišné podpisy jsou povoleny ve stejném prostoru deklarace (§7.6).
- Dvě nebo více deklarací typu se stejným názvem, ale různá čísla parametrů typu jsou povolena ve stejném prostoru deklarace (§7.8.2).
- Dvě nebo více deklarací typů s částečným modifikátorem ve stejném prostoru deklarace mohou sdílet stejný název, stejný počet parametrů typu a stejnou klasifikaci (třída, struktura nebo rozhraní). V tomto případě deklarace typu přispívají k jednomu typu a jsou samy agregovány tak, aby vytvořily jeden prostor prohlášení (§15.2.7).
- Deklarace oboru názvů a deklarace typu ve stejném prostoru deklarace mohou sdílet stejný název, pokud deklarace typu obsahuje alespoň jeden parametr typu (§7.8.2).
Existuje několik různých typů prostorů deklarací, jak je popsáno v následujícím příkladu.
- Ve všech kompilačních jednotkách programu jsou namespace_member_declarations bez uzavření namespace_declaration členy jednoho kombinovaného prostoru deklarace označovaného jako globální prostor deklarace.
- Ve všech kompilačních jednotkách programu jsou namespace_member_declarationv rámci namespace_declarations, které mají stejný plně kvalifikovaný název oboru názvů, členy jednoho kombinovaného prostoru deklarace.
- Každý compilation_unit a namespace_body má prostor deklarace aliasu. Každý extern_alias_directive a using_alias_directivecompilation_unit nebo namespace_body přispívá člena do prostoru prohlášení aliasu (§14.5.2).
- Každá nečástečně třídou, strukturou nebo deklarací rozhraní vytvoří nový prostor deklarace. Každá částečná třída, struktura nebo deklarace rozhraní přispívá k prostoru deklarace sdílenému všemi odpovídajícími částmi ve stejném programu (§16.2.4). Názvy se do tohoto prostoru deklarace zavádějí prostřednictvím class_member_declarations, struct_member_declarations, interface_member_declarations nebo type_parameters. S výjimkou přetížených deklarací konstruktoru instance a deklarací statického konstruktoru nemůže třída, struktura nebo rozhraní obsahovat deklaraci člena se stejným názvem jako třída, struktura nebo rozhraní. Třída, struktura nebo rozhraní umožňují deklaraci přetížených metod a indexerů. Třída nebo struktura navíc umožňuje deklaraci přetížených konstruktorů a operátorů instance. Například třída, struktura nebo rozhraní mohou obsahovat více deklarací metody se stejným názvem za předpokladu, že se tyto deklarace metody liší v podpisu (§7.6). Všimněte si, že základní třídy nepřispívají do deklarací prostoru třídy a základní rozhraní nepřispívají do prostoru deklarace rozhraní. Odvozená třída nebo rozhraní proto může deklarovat člen se stejným názvem jako zděděný člen. Takový člen je řečeno, aby skrýval zděděný člen.
- Každá deklarace delegáta vytvoří nový prostor deklarace. Názvy se do tohoto prostoru deklarace zavádějí prostřednictvím parametrů (fixed_parameters a parameter_array) a type_parameters.
- Každá deklarace výčtu vytvoří nový prostor deklarace. Názvy se do tohoto prostoru deklarace zavádějí prostřednictvím enum_member_declarations.
- Každá deklarace metody, deklarace vlastnosti, deklarace objektu vlastností, deklarace indexeru, deklarace přístupového objektu indexeru, deklarace operátoru, deklarace konstruktoru instance, anonymní funkce a místní funkce vytvoří nový prostor deklarace, který se nazývá prostor deklarace místní proměnné. Názvy se do tohoto prostoru deklarace zavádějí prostřednictvím parametrů (fixed_parameters a parameter_array) a type_parameters. Objekt set pro vlastnost nebo indexer zavádí název
valuejako parametr. Tělo člena funkce, anonymní funkce nebo místní funkce, pokud existuje, se považuje za vnořené do prostoru deklarace místní proměnné. Pokud prostor deklarace místní proměnné a vnořený prostor deklarace místní proměnné obsahují prvky se stejným názvem, v rámci oboru vnořeného místního názvu je vnější místní název skrytý (§7.7.1) podle vnořeného místního názvu. - V rámci deklarací členů, anonymních funkcí a místních funkcí může dojít k dalším prostorům deklarace místních proměnných. Názvy se do těchto deklarací zavádějí prostřednictvím vzorůs, declaration_expressions, declaration_statements a exception_specifiers. Prostory deklarace místních proměnných mohou být vnořené, ale jedná se o chybu pro prostor deklarace místní proměnné a vnořený prostor deklarace místních proměnných, který bude obsahovat prvky se stejným názvem. Proto v rámci vnořeného prostoru deklarace není možné deklarovat místní proměnnou, místní funkci nebo konstantu se stejným názvem jako parametr, parametr typu, místní proměnnou, místní funkci nebo konstantu v uzavřeném prostoru deklarace. Dva prostory deklarací můžou obsahovat prvky se stejným názvem, pokud druhý prostor deklarace neobsahuje. Místní prostory deklarací se vytvářejí pomocí následujících konstruktorů:
- Každá variable_initializer v poli a deklaraci vlastnosti zavádí vlastní prostor deklarace místní proměnné, který není vnořený do žádného jiného prostoru deklarace místní proměnné.
- Tělo člena funkce, anonymní funkce nebo místní funkce, pokud existuje, vytvoří místní prostor deklarace proměnné, který je považován za vnořený do prostoru deklarace místní proměnné funkce.
- Každý constructor_initializer vytvoří prostor deklarace místní proměnné vnořený v deklaraci konstruktoru instance. Prostor deklarace místní proměnné pro tělo konstruktoru je zase vnořený do tohoto prostoru deklarace místní proměnné.
- Každý blok, switch_block, specific_catch_clause, iteration_statement a using_statement vytvoří vnořený prostor deklarace místní proměnné.
- Každý embedded_statement , který není přímo součástí statement_list vytvoří vnořený prostor deklarace místní proměnné.
- Každý switch_section vytvoří vnořený prostor deklarace místní proměnné. Proměnné deklarované přímo v statement_listswitch_section (ale ne vnořeném prostoru deklarace místních proměnných uvnitř statement_list) se přidají přímo do prostoru deklarace místní proměnné ohraničujícího switch_block místo switch_section.
- Syntaktický překlad query_expression (§12.22.3) může zavést jeden nebo více výrazů lambda. Jako anonymní funkce vytvoří každý z nich prostor deklarace místní proměnné, jak je popsáno výše.
- Každý blok nebo switch_block vytvoří samostatný prostor deklarace popisků. Názvy se do tohoto prostoru deklarace zavádějí prostřednictvím labeled_statements a na názvy se odkazuje prostřednictvím goto_statements. Prostor deklarace popisku bloku zahrnuje všechny vnořené bloky. Proto v rámci vnořeného bloku není možné deklarovat popisek se stejným názvem jako popisek v uzavřeném bloku.
Poznámka: Skutečnost, že proměnné deklarované přímo v switch_section jsou přidány do prostoru deklarace místní proměnné switch_block místo switch_section může vést k překvapivý kód. V následujícím příkladu je místní proměnná
yv oboru v oddílu přepínače pro výchozí případ, i když se deklarace zobrazí v oddílu přepínače pro případ 0. Místní proměnnáznení v oboru v oddílu přepínače pro výchozí případ, protože je zavedena v prostoru deklarace místní proměnné pro oddíl přepínače, ve kterém k deklaraci dochází.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; }koncová poznámka
Textové pořadí, ve kterém jsou názvy deklarovány, je obecně bez významnosti. Zejména textové pořadí není významné pro deklaraci a použití oborů názvů, konstant, metod, vlastností, událostí, indexerů, operátorů, konstruktorů instancí, finalizátorů, statických konstruktorů a typů. Pořadí deklarací je významné následujícími způsoby:
- Pořadí prohlášení pro deklarace polí určuje pořadí, ve kterém se provádějí inicializátory (pokud existují) (§15.5.6.2, §15.5.6.3).
- Místní proměnné musí být definovány před jejich používáním (§7.7).
- Pořadí prohlášení pro deklarace členů výčtu (§20.4) je významné, pokud jsou vynechány constant_expression hodnoty.
Příklad: Prostor deklarace oboru názvů je otevřený a dvě deklarace oboru názvů se stejným plně kvalifikovaným názvem přispívají do stejného prostoru deklarace. Například
namespace Megacorp.Data { class Customer { ... } } namespace Megacorp.Data { class Order { ... } }Dvě deklarace oboru názvů výše přispívají ke stejnému prostoru deklarace, v tomto případě deklarují dvě třídy s plně kvalifikovanými názvy
Megacorp.Data.CustomeraMegacorp.Data.Order. Vzhledem k tomu, že dvě deklarace přispívají do stejného prostoru deklarace, způsobila by chybu v době kompilace, pokud každá obsahovala deklaraci třídy se stejným názvem.konec příkladu
Poznámka: Jak je uvedeno výše, deklarací prostoru bloku zahrnuje všechny vnořené bloky. Proto v následujícím příkladu
Fzpůsobí aGmetody chybu v době kompilace, protože názevije deklarován ve vnějším bloku a nelze ho předefinovat ve vnitřním bloku. TytoHmetodyIjsou však platné, protože tyto dvěijsou deklarovány v samostatných nenořených blocích.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(); } } }koncová poznámka
7.4 Členové
7.4.1 Obecné
Obory názvů a typy mají členy.
Poznámka: Členové entity jsou obecně k dispozici prostřednictvím použití kvalifikovaného názvu, který začíná odkazem na entitu, za kterým následuje token "
." následovaný názvem člena. koncová poznámka
Členy typu jsou buď deklarovány v deklaraci typu, nebo zděděné ze základní třídy typu. Když typ dědí ze základní třídy, všechny členy základní třídy s výjimkou konstruktorů instancí, finalizátorů a statických konstruktorů se stanou členy odvozeného typu. Deklarovaná přístupnost člena základní třídy neřídí, zda je člen zděděný – dědičnost se vztahuje na libovolný člen, který není konstruktorem instance, statickým konstruktorem ani finalizátorem.
Poznámka: Zděděný člen však nemusí být přístupný v odvozeném typu, například z důvodu deklarované přístupnosti (§7.5.2). koncová poznámka
7.4.2 Členy oboru názvů
Obory názvů a typy, které nemají uzavřený obor názvů, jsou členy globálního oboru názvů. To odpovídá přímo názvům deklarovaným v globálním prostoru deklarace.
Obory názvů a typy deklarované v rámci oboru názvů jsou členy tohoto oboru názvů. To odpovídá přímo názvům deklarovaným v prostoru deklarace oboru názvů.
Obory názvů nemají žádná omezení přístupu. Privátní, chráněné ani interní obory názvů není možné deklarovat a názvy oborů názvů jsou vždy veřejně přístupné.
7.4.3 Členy struktury
Členy struktury jsou členy deklarované ve struktuře a členy zděděné z přímé základní třídy System.ValueType struktury a nepřímé základní třídy object.
Členy jednoduchého typu přímo odpovídají členům typu struktury aliasu jednoduchého typu (§8.3.5).
7.4.4 – členy výčtu
Členy výčtu jsou konstanty deklarované v výčtu a členy zděděné z přímé základní třídy výčtu a nepřímé základní třídy System.EnumSystem.ValueType a object.
7.4.5 Členy třídy
Členy třídy jsou členy deklarované ve třídě a členy zděděné ze základní třídy (s výjimkou třídy object , která nemá žádnou základní třídu). Členy zděděné ze základní třídy zahrnují konstanty, pole, metody, vlastnosti, události, indexery, operátory a typy základní třídy, ale ne konstruktory instance, finalizátory a statické konstruktory základní třídy. Členové základní třídy se dědí bez ohledu na jejich přístupnost.
Deklarace třídy může obsahovat deklarace konstant, polí, metod, vlastností, událostí, indexerů, operátorů, konstruktorů instancí, finalizátorů, statických konstruktorů a typů.
Členové object (§8.2.3) a string (§8.2.5) přímo odpovídají členům typů tříd, které aliasují.
7.4.6 Členy rozhraní
Členy rozhraní jsou členy deklarované v rozhraní a ve všech základních rozhraních rozhraní.
Poznámka: Členové třídy
objectnejsou, přísně řečeno, členy jakéhokoli rozhraní (§19.4). Členy třídyobjectjsou však k dispozici prostřednictvím vyhledávání členů v libovolném typu rozhraní (§12.5). koncová poznámka
7.4.7 Maticové členy
Členy pole jsou členy zděděné z třídy System.Array.
7.4.8 Delegáti členové
Delegát dědí členy z třídy System.Delegate. Kromě toho obsahuje metodu s Invoke názvem se stejným návratovým typem a seznamem parametrů zadaným v prohlášení (§21.2). Vyvolání této metody se chová stejně jako vyvolání delegáta (§21.6) na stejné instanci delegáta.
Implementace může poskytovat další členy, a to buď prostřednictvím dědičnosti, nebo přímo v samotném delegátu.
7.5 Přístup členů
7.5.1 Obecné
Deklarace členů umožňují kontrolu nad přístupem členů. Přístupnost člena je stanovena deklarovanou přístupností (§7.5.2) člena v kombinaci s přístupností bezprostředně obsahujícího typu, pokud existuje.
Pokud je přístup k určitému členu povolen, je mu řečeno, že je přístupný. Naopak, když je přístup ke konkrétnímu členu zakázán, je tento člen nepřístupný. Přístup k členu je povolen, pokud textové umístění, ve kterém se přístup provádí, je součástí domény přístupnosti (§7.5.3) člena.
7.5.2 Deklarovaná přístupnost
Deklarovaná přístupnost člena může být jedna z následujících možností:
- Veřejná, která je vybrána zahrnutím
publicmodifikátoru do deklarace členu. Intuitivní význampublicje "přístup není omezen". - Chráněno, které je vybráno zahrnutím
protectedmodifikátoru do deklarace členu. Intuitivní významprotectedje "přístup omezen na obsahující třídu nebo rozhraní, nebo třídy nebo rozhraní odvozené z typu". - Interní, který je vybrán zahrnutím
internalmodifikátoru do deklarace členu. Intuitivní významinternalje "přístup omezený na toto sestavení". - Chráněná vnitřní, která je vybrána zahrnutím
protecteda modifikátoruinternalv deklaraci členu. Intuitivní významprotected internalje "přístupný v rámci tohoto sestavení a také typy odvozené z obsahující třídy". - Privátní ochrana, která je vybrána zahrnutím
privatemodifikátoruprotectedi modifikátoru v deklaraci členu. Intuitivní významprivate protectedje "přístupný v rámci tohoto sestavení pomocí obsahující třídy a typů odvozených z obsahující třídy". - Soukromé, které je vybráno zahrnutím
privatemodifikátoru do deklarace členu. Intuitivní významprivateje "přístup omezený na typ obsahující".
V závislosti na kontextu, ve kterém probíhá deklarace člena, jsou povoleny pouze určité typy deklarované přístupnosti. Navíc pokud deklarace členu neobsahuje žádné modifikátory přístupu, kontext, ve kterém deklarace probíhá, určuje výchozí deklarovanou přístupnost.
- Obory názvů implicitně deklarovaly
publicpřístupnost. U deklarací oboru názvů nejsou povoleny žádné modifikátory přístupu. - Typy deklarované přímo v kompilačních jednotkách nebo oborech názvů (na rozdíl od jiných typů) můžou mít
publicnebointernaldeklarovat přístupnost a výchozí nastaveníinternaldeklarované přístupnosti. - Členové třídy můžou mít některý z povolených typů deklarovaných přístupnosti a výchozí nastavení
privatedeklarovaných přístupností.Poznámka: Typ deklarovaný jako člen třídy může mít kterýkoli z povolených typů deklarované přístupnosti, zatímco typ deklarovaný jako člen oboru názvů může mít pouze
publicnebointernaldeklarovanou přístupnost. koncová poznámka - Členy struktury můžou mít
public,internalneboprivatedeklarovanou přístupnost a výchozíprivatenastavení pro deklarovanou přístupnost, protože struktury jsou implicitně zapečetěné. Členy struktury zavedené vstruct(tj. neděděné tímto strukturou) nemohou mítprotected,protected internalaniprivate protecteddeklarované přístupnosti.Poznámka: Typ deklarovaný jako člen struktury může mít
public,internalneboprivatedeklarovanou přístupnost, zatímco typ deklarovaný jako člen oboru názvů může mít pouzepublicnebointernaldeklarovanou přístupnost. koncová poznámka - Členové rozhraní implicitně deklarovali
publicpřístupnost. U deklarací členů rozhraní nejsou povoleny žádné modifikátory přístupu. - Členové výčtu implicitně deklarovali
publicpřístupnost. U deklarací členů výčtu nejsou povoleny žádné modifikátory přístupu.
7.5.3 Domény přístupnosti
Doména přístupnosti člena se skládá z částí (pravděpodobně nesouvislého) textu programu, ve kterých je povolen přístup k členu. Pro účely definování domény přístupnosti člena je člen označen jako nejvyšší úroveň, pokud není deklarován v rámci typu, a člen je vnořený, pokud je deklarován v jiném typu. Kromě toho je text programu definován jako veškerý text obsažený ve všech kompilačních jednotkách programu a text programu typu je definován jako veškerý text obsažený v type_declarationtohoto typu (včetně typů vnořených v daném typu).
Doména přístupnosti předdefinovaného typu (například object, intnebo double) je neomezená.
Doména přístupnosti nevázaného typu T nejvyšší úrovně (§8.4.4), která je deklarována v programu P , je definována takto:
- Pokud je deklarovaná přístupnost
Tveřejná, doménaTpřístupnosti je textPprogramu a jakýkoli program, který odkazujeP. - Pokud je deklarovaná přístupnost
Tinterní, doménaTpřístupnosti je textPprogramu .
Poznámka: Z těchto definic vyplývá, že doména přístupnosti nevázaného typu nejvyšší úrovně je vždy alespoň text programu programu, ve kterém je tento typ deklarován. koncová poznámka
Doména přístupnosti pro konstruovaný typ T<A₁, ..., Aₑ> je průsečíkem domény přístupnosti nevázaného obecného typu T a domén přístupnosti argumentů A₁, ..., Aₑtypu .
Doména přístupnosti vnořeného člena M deklarovaného v typu T v rámci programu Pje definována následujícím způsobem (a to znamená, že M samotný může být typem):
- Pokud je deklarovaná přístupnost
M, doménapublicpřístupnosti je doména přístupnosti doményM.T - Pokud je deklarovaná přístupnost
M, nechteprotected internalbýt sjednocením textuDprogramu a text programu libovolného typu odvozeného zP, který je deklarován mimoT.PDoménaMpřístupnosti je průnikem doményTDpřístupnosti s . - Pokud je deklarovaná přístupnost
M, nechteprivate protectedbýt průnikem textuDprogramu a textuPprogramu a jakéhokoli typu odvozeného zT.TDoménaMpřístupnosti je průnikem doményTDpřístupnosti s . - Pokud je deklarovaná přístupnost
M, nechteprotectedbýt sjednocením textuDprogramu a text programu libovolného typu odvozeného zT.TDoménaMpřístupnosti je průnikem doményTDpřístupnosti s . - Pokud je deklarovaná přístupnost
Minternal, doménaMpřístupnosti je průnikem doményTpřístupnosti s textemPprogramu . - Pokud je deklarovaná přístupnost
Mprivate, doménaMpřístupnosti je textTprogramu .
Poznámka: Z těchto definic následuje, že doména přístupnosti vnořeného člena je vždy alespoň text programu typu, ve kterém je člen deklarován. Dále platí, že doména přístupnosti člena není nikdy inkluzivnější než doména přístupnosti typu, ve kterém je člen deklarován. koncová poznámka
Poznámka: V intuitivních termínech se při přístupu k typu nebo členu
Mvyhodnotí následující kroky, aby se zajistilo, že je přístup povolený:
- Za prvé, pokud
Mje deklarován v rámci typu (na rozdíl od kompilační jednotky nebo oboru názvů), dojde k chybě v době kompilace, pokud tento typ není přístupný.- Pokud
Manopublic, přístup je povolený.- V opačném případě
Mprotected internalje přístup povolen v případě, že dojde v rámci programu, ve kterémMje deklarováno, nebo pokud dojde v rámci třídy odvozené od třídy, ve kteréMje deklarováno a probíhá prostřednictvím odvozeného typu třídy (§7.5.4).- V opačném případě
Mprotectedje přístup povolen v případě, že dojde v rámci třídy, ve kteréMje deklarováno, nebo pokud dojde v rámci třídy odvozené od třídy, ve kteréMje deklarováno a probíhá prostřednictvím odvozeného typu třídy (§7.5.4).- V opačném případě je přístup povolen,
Minternalpokud dojde v rámci programu, ve kterémMje deklarován.- V opačném případě je přístup povolen,
Mprivatepokud se vyskytuje v rámci typu, ve kterémMje deklarován.- V opačném případě je typ nebo člen nepřístupný a dojde k chybě v době kompilace. koncová poznámka
Příklad: V následujícím kódu
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; } }třídy a členové mají následující domény přístupnosti:
- Doména
Apřístupnosti aA.Xje neomezená.- Doména
A.Ypřístupnosti , ,BB.X,B.YB.C, ,B.C.XaB.C.Yje programový text obsahující program.- Doména
A.Zpřístupnosti je textAprogramu .- Doména
B.Zpřístupnosti aB.Dje textBprogramu , včetně textuB.Cprogramu aB.D.- Doména
B.C.Zpřístupnosti je textB.Cprogramu .- Doména
B.D.Xpřístupnosti aB.D.Yje textBprogramu , včetně textuB.Cprogramu aB.D.- Doména
B.D.Zpřístupnosti je textB.Dprogramu . Jak ukazuje příklad, doména přístupnosti člena není nikdy větší než doména obsahujícího typu. I když mají například všichniXčlenové veřejně deklarovanou přístupnost, všechnyA.Xdomény přístupnosti, které jsou omezené typem obsahujícího.konec příkladu
Jak je popsáno v §7.4, všechny členy základní třídy s výjimkou konstruktorů instancí, finalizátorů a statických konstruktorů jsou zděděny odvozenými typy. To zahrnuje i soukromé členy základní třídy. Doména přístupnosti soukromého člena však obsahuje pouze text programu typu, ve kterém je člen deklarován.
Příklad: V následujícím kódu
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 } }třída
Bdědí soukromý členxzAtřídy. Vzhledem k tomu, že člen je soukromý, je přístupný pouze v rámci class_bodyA. Přístup kb.xmetoděA.Ftedy proběhne úspěšně, ale vB.Fmetodě selže.konec příkladu
7.5.4 Chráněný přístup
protected
private protected Je-li člen instance přístupný mimo text programu třídy, ve které je deklarován, a když protected internal je člen instance přístupný mimo text programu programu, ve kterém je deklarován, bude přístup probíhat v deklaraci třídy, která je odvozena od třídy, ve které je deklarována. Kromě toho se vyžaduje přístup prostřednictvím instance tohoto odvozeného typu třídy nebo typu třídy vytvořeného z něj. Toto omezení zabraňuje, aby jedna odvozená třída přistupovala k chráněným členům jiných odvozených tříd, i když jsou členy zděděné ze stejné základní třídy. K členům rozhraní instance definovaným s protected přístupem nebo private protected k přístupu nelze získat přístup z class rozhraní nebo struct které implementuje toto rozhraní. K těmto rozhraním lze přistupovat pouze z odvozených rozhraní.
class Typy struct však mohou definovat přepsané protected členy instance deklarované v rozhraní, které implementuje.
Pojďme B být základní třídou, která deklaruje člen Mchráněné instance , a nechat D být třídou, která je odvozena z B.
V rámci class_bodyDmůže přístup k M jedné z následujících forem:
- Nekvalifikovaný type_name nebo primary_expression formuláře
M. - Primary_expression
- Primary_expression formuláře
base.M. - Primary_expression formuláře
base[argument_list].
Kromě těchto forem přístupu může odvozená třída přistupovat k konstruktoru chráněné instance základní třídy v constructor_initializer (§15.11.2).
Příklad: V následujícím kódu
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 } }v rámci
A, je možné přistupovatxprostřednictvím instancí obouAaB, protože v obou případech se přístup provádí prostřednictvím instanceAnebo třídy odvozené zA. V rámciBvšak není možné přistupovatxprostřednictvím instanceA, protožeAnení odvozen zB.konec příkladu
Příklad:
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"; } }V tomto případě jsou povolena
xtři přiřazení, protože se provádějí prostřednictvím instancí typů tříd vytvořených z obecného typu.konec příkladu
Poznámka: Doména přístupnosti (§7.5.3) chráněného člena deklarovaného v obecné třídě obsahuje text programu všech deklarací tříd odvozených z jakéhokoli typu vytvořeného z této obecné třídy. V tomto příkladu:
class C<T> { protected static T x; } class D : C<string> { static void Main() { C<int>.x = 5; } }odkaz na
protectedčlenaC<int>.xDje platný, i když třídaDje odvozena odC<string>. koncová poznámka
7.5.5 Omezení přístupnosti
Několik konstruktorů v jazyce C# vyžaduje, aby byl typ alespoň tak přístupný jako člen nebo jiný typ.
T Typ se říká, aby byl alespoň tak přístupný jako člen nebo typM, pokud je doména T přístupnosti nadmnožinou domény přístupnosti domény M. Jinými slovy, je alespoň tak přístupná, T jako M kdyby T byla přístupná ve všech kontextech, ve kterých M je přístupná.
Existují následující omezení přístupnosti:
- Přímá základní třída typu třídy musí být alespoň tak přístupná jako samotný typ třídy.
- Explicitní základní rozhraní typu rozhraní musí být alespoň tak přístupná jako samotný typ rozhraní.
- Návratový typ a typy parametrů typu delegáta musí být alespoň tak přístupné jako samotný typ delegáta.
- Typ konstanty musí být alespoň tak přístupný jako samotná konstanta.
- Typ pole musí být alespoň tak přístupný jako samotné pole.
- Návratový typ a typy parametrů metody musí být alespoň tak přístupné jako samotná metoda.
- Typ vlastnosti musí být alespoň tak přístupný jako samotná vlastnost.
- Typ události musí být alespoň tak přístupný jako samotná událost.
- Typ a typy parametrů indexeru musí být alespoň tak přístupné jako samotný indexer.
- Návratový typ a typy parametrů operátoru musí být alespoň tak přístupné jako samotný operátor.
- Typy parametrů konstruktoru instance musí být alespoň tak přístupné jako samotný konstruktor instance.
- Omezení typu rozhraní nebo třídy pro parametr typu musí být alespoň tak přístupné jako člen, který deklaruje omezení.
Příklad: V následujícím kódu
class A {...} public class B: A {...}výsledkem
Btřídy je chyba v době kompilace, protožeAnení alespoň tak přístupná jakoB.konec příkladu
Příklad: Podobně v následujícím kódu
class A {...} public class B { A F() {...} internal A G() {...} public A H() {...} }Výsledkem
HmetodyBje chyba v době kompilace, protože návratový typAnení alespoň tak přístupný jako metoda.konec příkladu
7.6 Podpisy a přetížení
Metody, konstruktory instancí, indexery a operátory jsou charakterizovány jejich podpisy:
- Podpis metody se skládá z názvu metody, počtu parametrů typu a typu a režimu předávání parametrů každého z jeho parametrů, který se považuje v pořadí zleva doprava. Pro tyto účely není žádný parametr typu metody, který se vyskytuje v typu parametru, identifikován jeho názvem, ale podle jeho pořadové pozice v seznamu parametrů typu metody. Podpis metody konkrétně neobsahuje návratový typ, názvy parametrů, názvy parametrů, názvy parametrů typu, omezení parametrů typu,
paramsmodifikátory parametrů anithisto, zda jsou parametry povinné nebo volitelné. - Podpis konstruktoru instance se skládá z typu a režimu předávání parametrů každého z jeho parametrů, který se považuje v pořadí zleva doprava. Podpis konstruktoru instance výslovně neobsahuje
paramsmodifikátor, který může být zadán pro parametr nejvíce vpravo, ani zda jsou parametry povinné nebo volitelné. - Podpis indexeru se skládá z typu každého z jeho parametrů, který se považuje v pořadí zleva doprava. Podpis indexeru konkrétně neobsahuje typ prvku, ani neobsahuje
paramsmodifikátor, který může být zadán pro parametr nejvíce vpravo, ani zda jsou parametry povinné nebo volitelné. - Podpis operátoru se skládá z názvu operátoru a typu každého z jeho parametrů, který je považován za pořadí zleva doprava. Podpis operátoru výslovně neobsahuje typ výsledku.
- Podpis operátoru převodu se skládá ze zdrojového typu a cílového typu. Implicitní nebo explicitní klasifikace operátoru převodu není součástí podpisu.
- Dva podpisy stejného typu členu (metoda, konstruktor instance, indexer nebo operátor) jsou považovány za stejné podpisy , pokud mají stejný název, počet parametrů typu, počet parametrů a režimy předávání parametrů a převod identity existuje mezi typy odpovídajících parametrů (§10.2.2).
Podpisy představují mechanismus pro přetížení členů ve třídách, strukturách a rozhraních:
- Přetížení metod umožňuje deklarovat více metod se stejným názvem třídy, struktury nebo rozhraní za předpokladu, že jejich podpisy jsou jedinečné v rámci této třídy, struktury nebo rozhraní.
- Přetížení konstruktorů instancí umožňuje deklarovat více konstruktorů instance, za předpokladu, že jejich podpisy jsou jedinečné v rámci této třídy nebo struktury.
- Přetížení indexerů umožňuje deklarovat více indexerů třídy, struktury nebo rozhraní za předpokladu, že jejich podpisy jsou jedinečné v rámci této třídy, struktury nebo rozhraní.
- Přetížení operátorů umožňuje deklarovat více operátorů se stejným názvem, pokud jsou jejich podpisy v rámci této třídy nebo struktury jedinečné.
Ačkoli inmodifikátory a outref modifikátory parametrů jsou považovány za součást podpisu, členy deklarované v jednom typu se nemohou lišit pouze inv podpisu , outa ref. K chybě v době kompilace dochází, pokud jsou dva členy deklarovány ve stejném typu s podpisy, které by byly stejné, pokud byly všechny parametry v obou metodách s out modifikátory nebo in modifikátory změněny na ref modifikátory. Pro jiné účely porovnávání podpisů (např. skrytí nebo přepsání) inouta ref jsou považovány za součást podpisu a vzájemně se neshodují.
Poznámka: Toto omezení umožňuje snadné překladu programů jazyka C# pro spouštění na platformě, která neposkytuje způsob, jak definovat metody, které se liší pouze v
in,outaref. koncová poznámka
Typy object a dynamic při porovnávání podpisů se nerozlišují. Členové deklarovaní v jednom typu, jehož podpisy se liší pouze nahrazením objectdynamic , nejsou povoleny.
Příklad: Následující příklad ukazuje sadu deklarací přetížené metody spolu s jejich podpisy.
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 }Všimněte si, že všechny
inmodifikátory ,outarefparametru (§15.6.2) jsou součástí podpisu. Proto ,F(int),F(in int),F(out int)aF(ref int)jsou všechny jedinečné podpisy. Však , a nelze deklarovat v rámci stejného rozhraní,F(in int)protože jejich podpisy se liší pouzeF(out int),F(ref int)ain.outrefVšimněte si také, že návratový typ aparamsmodifikátor nejsou součástí podpisu, takže není možné přetížit pouze na základě návratového typu nebo zahrnutí nebo vyloučení modifikátoruparams. Deklarace metodF(int)aF(params string[])identifikovaných výše mají za následek chybu v době kompilace. konec příkladu
7.7 Rozsahy
7.7.1 Obecné
Oborem názvu je oblast textu programu, ve které je možné odkazovat na entitu deklarovanou názvem bez kvalifikace názvu. Obory lze vnořit a vnitřní obor může předefinovat význam názvu z vnějšího oboru. (Toto omezení však neodebere § 7.3 , že v rámci vnořeného bloku není možné deklarovat místní proměnnou nebo místní konstantu se stejným názvem jako místní proměnná nebo místní konstanta v uzavřeném bloku.) Název z vnějšího oboru se pak říká, že je skrytý v oblasti textu programu, na který se vztahuje vnitřní obor, a přístup k vnějšímu názvu je možný pouze opravňujícím názvem.
Rozsah člena oboru názvů deklarovaného namespace_member_declaration (§14.6) bez uzavření namespace_declaration je celý text programu.
Rozsah člena oboru názvů deklarovaného namespace_member_declaration v rámci namespace_declaration , jehož plně kvalifikovaný název je
N, je namespace_body každého namespace_declaration , jehož plně kvalifikovaný název jeNnebo začínáNtečkou.Rozsah názvu definovaného extern_alias_directive (§14.4) se vztahuje na using_directives, global_attributes a namespace_member_declarationjeho bezprostředně obsahující compilation_unit nebo namespace_body. Extern_alias_directive nepřispívá do podkladového prostoru deklarace žádné nové členy. Jinými slovy, extern_alias_directive není tranzitivní, ale spíše ovlivňuje pouze compilation_unit nebo namespace_body , ve kterých k němu dochází.
Rozsah názvu definovaného nebo importovaného using_directive (§14.5) se vztahuje na global_attributes a namespace_member_declaration compilation_unit nebo namespace_body, ve kterých dochází k using_directive. Using_directive může zpřístupnit nula nebo více názvů oborů názvů nebo typů v rámci konkrétního compilation_unit nebo namespace_body, ale nepřispívá k podkladovému prostoru deklarace žádné nové členy. Jinými slovy, using_directive není tranzitivní, ale spíše ovlivňuje pouze compilation_unit nebo namespace_body, ve kterých dochází.
Rozsah parametru typu deklarovaného type_parameter_list na class_declaration (§15.2) je class_base, type_parameter_constraints_clausea class_body tohoto class_declaration.
Poznámka: Na rozdíl od členů třídy se tento obor nevztahuje na odvozené třídy. koncová poznámka
Rozsah parametru typu deklarovaného type_parameter_list na struct_declaration (§16.2) je struct_interfaces, type_parameter_constraints_clausea struct_body tohoto struct_declaration.
Rozsah parametru typu deklarovaného type_parameter_listinterface_declaration (§19.2) je interface_base, type_parameter_constraints_clauses a interface_body tohoto interface_declaration.
Rozsah parametru typu deklarovaného type_parameter_list na delegate_declaration (§21.2) je return_type, parameter_list a type_parameter_constraints_clausedelegate_declaration.
Rozsah parametru typu deklarovaného type_parameter_list na method_declaration (§15.6.1) je method_declaration.
Rozsahem člena deklarovaného class_member_declaration (§15.3.1) je class_body , ve kterém k prohlášení dochází. Kromě toho se rozsah člena třídy vztahuje na class_body odvozených tříd, které jsou zahrnuty do domény přístupnosti (§7.5.3) člena.
Rozsahem člena deklarovaného struct_member_declaration (§16.3) je struct_body , ve kterém se prohlášení vyskytuje.
Rozsahem člena deklarovaného enum_member_declaration (§20.4) je enum_body , ve kterém k prohlášení dochází.
Rozsahem parametru deklarovaného v method_declaration (§15.6) je method_body nebo ref_method_body tohoto method_declaration.
Rozsahem parametru deklarovaného v indexer_declaration (§15.9) je indexer_body tohoto indexer_declaration.
Rozsahem parametru deklarovaného v operator_declaration (§15.10) je operator_body tohoto operator_declaration.
Rozsahem parametru deklarovaného v constructor_declaration (§15.11) je constructor_initializer a blok této constructor_declaration.
Rozsahem parametru deklarovaného v lambda_expression (§12.21) je lambda_expression_body tohoto lambda_expression.
Rozsah parametru deklarovaného v anonymous_method_expression (§12.21) je blokem tohoto anonymous_method_expression.
Rozsah označení deklarovaného v labeled_statement (§13.5) je blok , ve kterém se prohlášení vyskytuje.
Rozsah místní proměnné deklarované v local_variable_declaration (§13.6.2) je blok , ve kterém se deklarace vyskytuje.
Rozsah místní proměnné deklarované v prohlášení (
switch) je switch_block.Rozsah místní proměnné deklarované v for_initializer
forprohlášení (§13.9.4) je for_initializer, for_condition, for_iterator a embedded_statementforprohlášení.Rozsah místní konstanty deklarované v local_constant_declaration (§13.6.3) je blok , ve kterém se deklarace vyskytuje. Jedná se o chybu v době kompilace odkazující na místní konstantu v textové pozici, která předchází jeho constant_declarator.
Rozsah proměnné deklarované jako součást foreach_statement, using_statement, lock_statement nebo query_expression je určen rozšířením daného konstruktoru.
V rámci oboru názvů, třídy, struktury nebo členu výčtu je možné odkazovat na člen v textové pozici, která předchází deklaraci člena.
Příklad:
class A { void F() { i = 1; } int i = 0; }Zde je platné
Fodkazovat předideklarování.konec příkladu
V rámci oboru místní proměnné se jedná o chybu v době kompilace odkazující na místní proměnnou v textové pozici, která předchází jeho deklarátoru.
Příklad:
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 } }Ve výše uvedené
Fmetodě první přiřazeníikonkrétně neodkazuje na pole deklarované ve vnějším oboru. Místo toho odkazuje na místní proměnnou a výsledkem je chyba v době kompilace, protože textově předchází deklaraci proměnné.GV metodě je použitíjinicializátoru pro deklaracijplatné, protože použití před deklarátorem není.HV metodě následný deklarátor správně odkazuje na místní proměnnou deklarovanou v dřívějším deklarátoru ve stejné local_variable_declaration.konec příkladu
Poznámka: Pravidla oborů pro místní proměnné a místní konstanty jsou navržena tak, aby zaručit, že význam názvu použitého v kontextu výrazu je vždy stejný v rámci bloku. Pokud by rozsah místní proměnné byl rozšířen pouze z jeho deklarace na konec bloku, pak v předchozím příkladu by první přiřazení přiřadilo proměnné instance a druhé přiřazení by přiřadilo místní proměnné, což by mohlo vést k chybám v době kompilace, pokud by příkazy bloku byly později přeuspořádané.)
Význam názvu v bloku se může lišit v závislosti na kontextu, ve kterém se název používá. V příkladu
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" } }název
Ase používá v kontextu výrazu odkazující na místní proměnnouAa v kontextu typu odkazující na tříduA.koncová poznámka
7.7.2 Název skrytí
7.7.2.1 Obecné
Rozsah entity obvykle zahrnuje více textu programu než prostor deklarace entity. Konkrétně může rozsah entity zahrnovat deklarace, které zavádějí nové prostory deklarace obsahující entity se stejným názvem. Tyto deklarace způsobí, že původní entita bude skrytá. Entita je naopak viditelná , když není skrytá.
Skrytí názvů nastane, když se obory překrývají prostřednictvím vnoření a když se obory překrývají prostřednictvím dědičnosti. Vlastnosti dvou typů skrývání jsou popsány v následujících dílčích nákládách.
7.7.2.2 Skrytí prostřednictvím vnoření
Ke skrytí názvů prostřednictvím vnoření může dojít v důsledku vnoření oborů názvů nebo typů v rámci oborů názvů v důsledku vnoření typů v rámci tříd nebo struktur, v důsledku místní funkce nebo lambda a v důsledku parametru, místní proměnné a místních deklarací konstant.
Příklad: V následujícím kódu
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; } }
Fv rámci metody je proměnnáiinstance skrytá místní proměnnoui, ale v rámciGmetody stáleiodkazuje na proměnnou instance. Uvnitř místní funkceM1float iskryje okamžitě vnějšíi. Parametrilambda skryjefloat ivnitřní část těla lambda.konec příkladu
Když název ve vnitřním oboru skryje název ve vnějším oboru, skryje všechny přetížené výskyty tohoto názvu.
Příklad: V následujícím kódu
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 } } }volání
F(1)vyvoláFdeklarované vInner, protože všechny vnější výskytyFjsou skryty vnitřní deklarací. Z stejného důvodu má voláníF("Hello")za následek chybu v době kompilace.konec příkladu
7.7.2.3 Skrytí prostřednictvím dědičnosti
K skrytí názvů prostřednictvím dědičnosti dochází, když třídy nebo struktury znovu předefinují názvy, které byly zděděny ze základních tříd. Tento typ skrývání názvů má jeden z následujících formulářů:
- Konstanta, pole, vlastnost, událost nebo typ zavedený ve třídě, struktuře nebo rozhraní skryje všechny členy základní třídy se stejným názvem.
- Metoda zavedená ve třídě, struktuře nebo rozhraní skryje všechny členy základní třídy bez metody se stejným názvem a všechny metody základní třídy se stejným podpisem (§7.6).
- Indexer zavedený ve třídě, struktuře nebo rozhraní skryje všechny indexery základního typu se stejným podpisem (§7.6).
Pravidla pro deklarace operátorů (§15.10) znemožňují deklarování operátoru se stejným podpisem jako operátor v základní třídě. Operátory tedy nikdy navzájem neuchovávají.
Na rozdíl od skrytí názvu z vnějšího oboru způsobí skrytí viditelného názvu z zděděného oboru upozornění.
Příklad: V následujícím kódu
class Base { public void F() {} } class Derived : Base { public void F() {} // Warning, hiding an inherited name }prohlášení
FoDerivedpříčinách nahlášení upozornění. Skrytí zděděného názvu není konkrétně chybou, protože by se tím zabránilo samostatnému vývoji základních tříd. Výše uvedená situace mohla například nastat, protože novější verzeBasezavedlaFmetodu, která nebyla přítomna ve starší verzi třídy.konec příkladu
Upozornění způsobené skrytím zděděného názvu lze odstranit pomocí modifikátoru new :
Příklad:
class Base { public void F() {} } class Derived : Base { public new void F() {} }
newModifikátor označuje, žeFinDerivedje "nový", a že je skutečně určen ke skrytí zděděného člena.konec příkladu
Deklarace nového členu skryje zděděný člen pouze v rámci rozsahu nového člena.
Příklad:
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 } }V předchozím příkladu deklarace
FinDerivedskryjeF, která byla zděděna zBase, ale vzhledem k tomu, že novýFinDerivedmá soukromý přístup, jeho rozsah se nevztahuje naMoreDerived.F()MoreDerived.GVolání je tedy platné a vyvolá .Base.Fkonec příkladu
7.8 Obor názvů a názvy typů
7.8.1 Obecné
Několik kontextů v programu jazyka C# vyžaduje zadání namespace_name nebo type_name .
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?)*
;
Namespace_name je namespace_or_type_name, která odkazuje na obor názvů.
V případě řešení, jak je popsáno níže, namespace_or_type_namenamespace_name odkazuje na obor názvů nebo jinak dojde k chybě v době kompilace. V namespace_name nemohou být přítomny žádné argumenty typu (§8.4.2) (argumenty typu mohou mít pouze typy).
Type_name je namespace_or_type_name, která odkazuje na typ.
V případě řešení, jak je popsáno níže, namespace_or_type_nametype_name odkazuje na typ nebo jinak dojde k chybě v době kompilace.
Namespace_or_type_name odkazuje na typ nebo obor názvů. Řešení konkrétního oboru názvů nebo typu zahrnuje dva kroky založené na rozdělení gramatiky na úvodní část, což je jeden z fragmentů gramatiky:
identifier type_argument_list?qualified_alias_member
a koncovou část, která je fragmentem gramatiky:
('.' identifier type_argument_list?)*
Nejdříve se řeší úvodní část k určení R₀, počátečního oboru názvů nebo typu.
Pokud je úvodní částí prvku namespace_or_type_namequalified_alias_member, poté je R₀ oborem názvů nebo typem, který je identifikován řešením, jak je popsáno v §14.8.1.
V opačném případě bude úvodní část, která je fragmentem gramatiky identifier type_argument_list?, mít jednu z následujících forem:
II<A₁, ..., Aₓ>
kde:
-
Ije jeden identifikátor; a -
<A₁, ..., Aₓ>je type_argument_list, pokud není zadaná žádná type_argument_list , zvažtexhodnotu nula.
R₀ je určen takto:
- Pokud
xje nula a namespace_or_type_name se zobrazí v deklaraci obecné metody (§15.6), ale mimo atributy hlavičkymetody, a pokud tato deklarace obsahuje parametr typu (§15.2.3) s názvemI, odkazujeR₀na tento parametr typu. - V opačném případě pokud se namespace_or_type_name zobrazí v deklaraci typu, pak pro každý typ
Tinstance (§15.3.2), počínaje typem instance této deklarace typu a pokračováním s typem instance každé nadřazené třídy, struktury nebo deklarace rozhraní (pokud existuje):- Pokud
xje nula a deklaraceTobsahuje parametr typu s názvemI, odkazujeR₀na tento parametr typu. - V opačném případě, pokud se namespace_or_type_name objeví v těle deklarace typu a pokud
Tnebo některý z jeho základních typů obsahuje vnořený přístupný typ s názvemIa parametry typux, pakR₀odkazuje na tento typ vytvořený s danými argumenty typu. Pokud existuje více než jeden takový typ, je vybrán typ deklarovaný v rámci více odvozených typů. Jedná se o chybu kompilátoru, pokud žádný typ není odvozenější než všechny ostatní.Poznámka: Netypové členy (konstanty, pole, metody, vlastnosti, indexery, operátory, konstruktory instancí, finalizátory a statické konstruktory) a členy typu s jiným počtem parametrů typu se při určování významu namespace_or_type_name ignorují. koncová poznámka
- Jinak platí, že pro každý obor názvů počínaje oborem názvů
N, ve kterém dojde k namespace_or_type_name , pokračujeme s každým uzavřeným oborem názvů (pokud existuje) a končíme globálním oborem názvů, následující kroky se vyhodnotí, dokud se neskončí entita:- Pokud
xje nula aIje název oboru názvů vN, pak:- Pokud je umístění, kde dojde k namespace_or_type_name , uzavřeno deklarací
Noboru názvů a deklarace oboru názvů obsahuje extern_alias_directive nebo using_alias_directive , které přidruží názevIk oboru názvů nebo typu, je namespace_or_type_name nejednoznačný a dojde k chybě v době kompilace. -
R₀V opačném případě odkazuje na obor názvů pojmenovanýIvN.
- Pokud je umístění, kde dojde k namespace_or_type_name , uzavřeno deklarací
- Jinak pokud
Nobsahuje přístupný typ s parametry názvuIaxtypu, pak:- Pokud
xje nula a umístění, kde dojde k namespace_or_type_name , je uzavřena deklarací oboru názvů aNdeklarace oboru názvů obsahuje extern_alias_directive nebo using_alias_directive , které přidruží názevIk oboru názvů nebo typu, pak je namespace_or_type_name nejednoznačný a dojde k chybě kompilace. -
R₀V opačném případě odkazuje na typ vytvořený s danými argumenty typu.
- Pokud
- V opačném případě, pokud umístění, kde namespace_or_type_name nastane, je uzavřena deklarace oboru názvů pro
N:- Pokud
xje nula a deklarace oboru názvů obsahuje extern_alias_directive nebo using_alias_directive , která přidruží názevIk importovanému oboru názvů nebo typu,R₀odkazuje na tento obor názvů nebo typ. - Jinak pokud obory názvů importované using_namespace_directivedeklarace oboru názvů obsahují přesně jeden typ s parametry názvu
Iaxtypu, odkazujeR₀na tento typ vytvořený s danými argumenty typu. - V opačném případě, pokud obory názvů importované pomocí using_namespace_directive v deklaraci oboru názvů obsahují více než jeden typ s názvem
Ia parametrů typux, pak je namespace_or_type_name nejednoznačný a dojde k chybě při kompilaci.
- Pokud
- Pokud
- V opačném případě je namespace_or_type_name nedefinovaný a dojde k chybě v době kompilace.
- Pokud
Pokud bylo R₀ vyřešeno, poté je vyřešena koncová část namespace_or_type_name. Koncový gramatický fragment se skládá z k ≥ 0 opakování, přičemž každé opakování dále rozlišuje odkazovaný obor názvů nebo typ.
Pokud k je nula, tj. neexistuje žádná koncová část, pak se namespace_or_type_name přeloží na R₀.
V opačném případě bude mít každé opakování jednu z těchto forem:
.I.I<A₁, ..., Aₓ>
kde I, A a x jsou definovány jako výše.
Pro každé opakování n, kde 1 ≤ n ≤ k, jeho rozlišení Rₙ, které zahrnuje Rₚ, kde p = n - 1, rozlišení předchozího opakování, je určeno takto:
- Pokud
xje nula aRₚodkazuje na obor názvů aRₚobsahuje vnořený obor názvů s názvemI,Rₙodkazuje na tento vnořený obor názvů. -
RₚPokud se v opačném případě odkazuje na obor názvů aRₚobsahuje přístupný typ s parametry názvuIaxtypu,Rₙodkazuje na tento typ vytvořený s danými argumenty typu. - V opačném případě, pokud
Rₚodkazuje na (pravděpodobně konstruovanou) třídu, strukturu nebo typ rozhraní aRₚnebo jakýkoli z jeho základních typů obsahují vnořený přístupný typ s názvemIaxparametry typu, pakRₙodkazuje na tento typ vytvořený s danými argumenty typu. Pokud existuje více než jeden takový typ, je vybrán typ deklarovaný v rámci více odvozených typů. Jedná se o chybu kompilátoru, pokud žádný typ není odvozenější než všechny ostatní.Poznámka: Je-li význam pro
T.Iurčitý typTurčen jako součást řešení specifikaceTzákladní třídy, je považovánaTza přímou základní tříduobject(§15.2.4.2). koncová poznámka - V opačném případě je namespace_or_type_name neplatný a dojde k chybě v době kompilace.
Řešení namespace_or_type_name je řešení konečného opakováníRₖ.
Namespace_or_type_name je povoleno odkazovat na statickou třídu (§15.2.2.4) pouze v případě, že
- Namespace_or_type_name je
Tve namespace_or_type_name formulářeT.I, nebo -
namespace_or_type_name je
Tve výrazu typu typeof (§12.8.18) ve tvarutypeof(T)
Příklad:
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 }V předchozím příkladu je volání
NestedClass.M()inC.Test()nejednoznačné meziB.NestedClass.M()aA.NestedClass.M()protože ani jeden z nich není odvozenější než druhý. Explicitní odkaz na buďA.NestedClass.M()neboB.NestedClass.M()je nutný k vyřešení nejednoznačnosti.konec příkladu
7.8.2 Nekvalifikované názvy
Každá deklarace oboru názvů a deklarace typu má nekvalifikovaný název určený následujícím způsobem:
- Pro deklaraci oboru názvů je nekvalifikovaný název qualified_identifier zadaný v deklaraci.
- Pro deklaraci typu bez type_parameter_list je nekvalifikovaný název identifikátor určený v deklaraci.
- U deklarace typu s parametry typu K je nekvalifikovaný název identifikátor specifikovaný v deklaraci následovaný generic_dimension_specifier (§12.8.18) pro parametry typu K.
7.8.3 Plně kvalifikované názvy
Každá deklarace oboru názvů a typu má plně kvalifikovaný název, který jednoznačně identifikuje obor názvů nebo deklaraci typu mezi všemi ostatními v rámci programu. Plně kvalifikovaný název deklarace oboru názvů nebo typu s nekvalifikovaným názvem N je určen takto:
- Pokud
Nje členem globálního oboru názvů, jeho plně kvalifikovaný název jeN. - V opačném případě je
S.Njeho plně kvalifikovaný název , kdeSje plně kvalifikovaný název oboru názvů nebo deklarace typu, ve kterémNje deklarován.
Jinými slovy, plně kvalifikovaný název N je úplná hierarchická cesta identifikátorů a generic_dimension_specifier, které vedou k N, počínaje globálním oborem názvů. Vzhledem k tomu, že každý člen oboru názvů nebo typu musí mít jedinečný název, následuje, že plně kvalifikovaný název oboru názvů nebo deklarace typu je vždy jedinečný. Jedná se o chybu v době kompilace pro stejný plně kvalifikovaný název odkazující na dvě odlišné entity. Zejména jde o toto:
- Jedná se o chybu deklarace oboru názvů i deklarace typu, která má stejný plně kvalifikovaný název.
- Jedná se o chybu dvou různých typů deklarací typu, které mají stejný plně kvalifikovaný název (například pokud mají deklarace struktury i třídy stejný plně kvalifikovaný název).
- Jedná se o chybu deklarace typu bez částečného modifikátoru, aby měl stejný plně kvalifikovaný název jako jiná deklarace typu (§15.2.7).
Příklad: Následující příklad ukazuje několik deklarací oboru názvů a typů spolu s přidruženými plně kvalifikovanými názvy.
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<> } }konec příkladu
7.9 Automatická správa paměti
Jazyk C# využívá automatickou správu paměti, která vývojářům umožňuje ruční přidělování a uvolnění paměti obsazené objekty. Zásady automatické správy paměti jsou implementovány uvolňováním paměti. Životní cyklus správy paměti objektu je následující:
- Při vytvoření objektu je pro něj přidělena paměť, konstruktor je spuštěn a objekt je považován za živý.
- Pokud k objektu ani žádnému z jeho polí instance nelze přistupovat žádným možným pokračováním provádění, kromě spuštění finalizátorů, objekt se považuje za nepoužíný a bude způsobilý k dokončení.
Poznámka: Kompilátor jazyka C# a systém uvolňování paměti se můžou rozhodnout analyzovat kód a určit, které odkazy na objekt se můžou v budoucnu použít. Pokud je například místní proměnná, která je v oboru jediným existujícím odkazem na objekt, ale tato místní proměnná se nikdy neodkazuje v žádném možném pokračování provádění z aktuálního bodu spuštění v postupu, může uvolňování paměti (ale není nutné) považovat objekt za nepoužitý. koncová poznámka
- Jakmile je objekt způsobilý k dokončení, v některých nespecifikovaných pozdějších časech finalizátor (§15.13) (pokud existuje) pro objekt je spuštěn. Za normálních okolností se finalizátor objektu spustí pouze jednou, i když rozhraní API definovaná implementací můžou toto chování povolit přepsání.
- Jakmile se finalizátor objektu spustí, nebude-li objekt ani žádná z jeho polí instance přístupná žádným možným pokračováním provádění, včetně spuštění finalizátorů, objekt je považován za nepřístupný a objekt se stane způsobilým pro kolekci.
Poznámka: Objekt, ke kterému dříve nelze získat přístup, může být opět přístupný z důvodu jeho finalizátoru. Příklad je uvedený níže. koncová poznámka
- A konečně, jakmile se objekt stane způsobilým pro shromažďování, uvolňování paměti uvolní paměť přidruženou k danému objektu.
Systém uvolňování paměti udržuje informace o využití objektu a používá tyto informace k rozhodování o správě paměti, jako je například umístění nově vytvořeného objektu v paměti, kdy se má objekt přemístit a kdy se objekt už nepoužívá nebo není nepřístupný.
Stejně jako jiné jazyky, které předpokládají existenci uvolňování paměti, je jazyk C# navržen tak, aby systém uvolňování paměti mohl implementovat širokou škálu zásad správy paměti. Jazyk C# určuje časové omezení v daném rozsahu ani pořadí, ve kterém se finalizační metody spouští. Zda jsou finalizátory spuštěny jako součást ukončení aplikace, je definována implementace (§7.2).
Chování uvolňování paměti lze do určité míry řídit pomocí statických metod třídy System.GC. Tuto třídu lze použít k vyžádání kolekce, aby došlo, finalizátory ke spuštění (nebo ne ke spuštění) atd.
Příklad: Vzhledem k tomu, že uvolňování paměti je povoleno široké zeměpisné šířky při rozhodování o tom, kdy shromažďovat objekty a spouštět finalizátory, může odpovídající implementace vytvořit výstup, který se liší od toho, co ukazuje následující kód. Program
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(); } }vytvoří instanci třídy
Aa instanci třídyB. Tyto objekty se stanou způsobilými pro uvolňování paměti, když je proměnnábpřiřazena hodnotunull, protože po této době není možné, aby k nim žádný kód napsaný uživatelem získal přístup. Výstup může být buďFinalize instance of A Finalize instance of Bnebo
Finalize instance of B Finalize instance of Avzhledem k tomu, že jazyk neukládá žádná omezení pořadí, ve kterém jsou objekty uvolněny z paměti.
V drobných případech může být důležitý rozdíl mezi "způsobilým k dokončení" a "způsobilým pro shromažďování". Příklad:
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"); } } }Pokud se ve výše uvedeném programu systém uvolňování paměti rozhodne spustit finalizátor před finalizátorem
AB, pak výstup tohoto programu může být:Finalize instance of A Finalize instance of B A.F RefA is not nullVšimněte si, že i když se instance
Anepoužívala aAfinalizační metoda byla spuštěna, je stále možné volat metodyA(v tomto případěF) z jiné finalizátoru. Všimněte si také, že spuštění finalizátoru může způsobit opětovné použití objektu z hlavního programu. V tomto případě spuštění finalizátoruBzpůsobilo, že instanceA, která se dříve nepoužívala, byla přístupná z živého odkazuTest.RefA. Po voláníWaitForPendingFinalizersje instanceBoprávněna pro kolekci, ale instanceAnení, protože odkazTest.RefA.konec příkladu
7.10 Pořadí provedení
Provádění programu jazyka C# pokračuje tak, aby vedlejší účinky každého spuštěného vlákna byly zachovány v kritických bodech spuštění.
Vedlejší účinek je definován jako čtení nebo zápis nestálého pole, zápis do nestálé proměnné, zápis do externího prostředku a vyvolání výjimky. Kritické prováděcí body, ve kterých se zachová pořadí těchto vedlejších účinků, jsou odkazy na nestálá pole (§15.5.4), lock prohlášení (§13.13) a vytvoření a ukončení vlákna. Spouštěcí prostředí je bezplatné změnit pořadí provádění programu jazyka C#, s výhradou následujících omezení:
- Závislost na datech je zachována v rámci vlákna provádění. To znamená, že hodnota každé proměnné se vypočítá jako kdyby byly všechny příkazy ve vlákně provedeny v původním pořadí programu.
- Pravidla řazení inicializace jsou zachována (§15.5.5, §15.5.6).
- Pořadí vedlejších účinků je zachováno s ohledem na nestálé čtení a zápisy (§15.5.4). Kromě toho spouštěcí prostředí nemusí vyhodnocovat část výrazu, pokud může odvodit, že hodnota tohoto výrazu se nepoužívá a že nejsou vytvořeny žádné potřebné vedlejší účinky (včetně jakýchkoli příčin způsobených voláním metody nebo přístupem k nestálým poli). Pokud je provádění programu přerušeno asynchronní událostí (například výjimkou vyvolanou jiným vláknem), není zaručeno, že pozorovatelné vedlejší účinky jsou viditelné v původním pořadí programu.
ECMA C# draft specification