7 Základní koncepty
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.Task
neboSystem.Threading.Tasks.Task<int>
. - Návratový typ musí být
void
,int
,System.Threading.Tasks.Task
neboSystem.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
async
musí mít přesně jeden ze dvou návratových typů uvedených výše, aby bylo možné kvalifikovat jako vstupní bod. Metodaasync void
neboasync
metoda vracející jiný typ awaitable, napříkladValueTask
neboValueTask<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
Main
parametrů metody. - Pokud je
Main
návratovýSystem.Threading.Tasks.Task
typ metody , návratový typ syntetizované metody jevoid
- Pokud je
Main
ná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
Main
a předává jejístring[]
hodnotu parametru jako argument,Main
pokud metoda má takový parametr. - Pokud metoda
Main
vyvolá 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
Main
s návratovým typemSystem.Threading.Tasks.Task<int>
, pokud úloha úspěšně dokončí,int
hodnota 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é jsou dány hodnotami definovanými implementací hostitelského prostředí před spuštěním aplikace. 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 int
platné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 null
nebo 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
Ukončení aplikace vrátí řízení do spouštěcího prostředí.
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 (§21.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.SuppressFinalize
knihovny). 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í (§18), výčtů (§19) a delegátů (§20). 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 nebo struktura obsahovat deklaraci členu se stejným názvem jako třída nebo struktura. 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
value
jako 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.20.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á
y
v 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áz
není 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 (§19.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.Customer
aMegacorp.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.end example
Poznámka: Jak je uvedeno výše, deklarací prostoru bloku zahrnuje všechny vnořené bloky. Proto v následujícím příkladu
F
způsobí aG
metody chybu v době kompilace, protože názevi
je deklarován ve vnějším bloku a nelze ho předefinovat ve vnitřním bloku. TytoH
metodyI
jsou však platné, protože tyto dvěi
jsou 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.Enum
System.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: Členy třídy
object
nejsou, přísně řečeno, členy jakéhokoli rozhraní (§18.4). Členy třídyobject
jsou 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 názvem Invoke
se stejným návratovým typem a seznamem parametrů zadaným v prohlášení (§20.2). Vyvolání této metody se chová stejně jako vyvolání delegáta (§20.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
public
modifikátoru do deklarace členu. Intuitivní význampublic
je "přístup není omezen". - Chráněno, které je vybráno zahrnutím
protected
modifikátoru do deklarace členu. Intuitivní významprotected
je "přístup omezený na obsahující třídu nebo typy odvozené z obsahující třídy". - Interní, který je vybrán zahrnutím
internal
modifikátoru do deklarace členu. Intuitivní významinternal
je "přístup omezený na toto sestavení". - Chráněná vnitřní, která je vybrána zahrnutím
protected
a modifikátoruinternal
v deklaraci členu. Intuitivní významprotected internal
je "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
private
modifikátoruprotected
i modifikátoru v deklaraci členu. Intuitivní významprivate protected
je "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
private
modifikátoru do deklarace členu. Intuitivní významprivate
je "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
public
pří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
public
nebointernal
deklarovat přístupnost a výchozí nastaveníinternal
deklarované přístupnosti. - Členové třídy můžou mít některý z povolených typů deklarovaných přístupnosti a výchozí nastavení
private
deklarovaný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
public
nebointernal
deklarovanou přístupnost. koncová poznámka - Členy struktury můžou mít
public
,internal
neboprivate
deklarovanou přístupnost a výchozíprivate
nastavení 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 internal
aniprivate protected
deklarované přístupnosti.Poznámka: Typ deklarovaný jako člen struktury může mít
public
,internal
neboprivate
deklarovanou přístupnost, zatímco typ deklarovaný jako člen oboru názvů může mít pouzepublic
nebointernal
deklarovanou přístupnost. koncová poznámka - Členové rozhraní implicitně deklarovali
public
přístupnost. U deklarací členů rozhraní nejsou povoleny žádné modifikátory přístupu. - Členové výčtu implicitně deklarovali
public
pří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
, int
nebo 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
T
veřejná, doménaT
přístupnosti je textP
programu a jakýkoli program, který odkazujeP
. - Pokud je deklarovaná přístupnost
T
interní, doménaT
přístupnosti je textP
programu .
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 P
je 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énapublic
přístupnosti je doména přístupnosti doményM
.T
- Pokud je deklarovaná přístupnost
M
, nechteprotected internal
být sjednocením textuD
programu a text programu libovolného typu odvozeného zP
, který je deklarován mimoT
.P
DoménaM
přístupnosti je průnikem doményT
D
přístupnosti s . - Pokud je deklarovaná přístupnost
M
, nechteprivate protected
být průnikem textuD
programu a textuP
programu a jakéhokoli typu odvozeného zT
.T
DoménaM
přístupnosti je průnikem doményT
D
přístupnosti s . - Pokud je deklarovaná přístupnost
M
, nechteprotected
být sjednocením textuD
programu a text programu libovolného typu odvozeného zT
.T
DoménaM
přístupnosti je průnikem doményT
D
přístupnosti s . - Pokud je deklarovaná přístupnost
M
internal
, doménaM
přístupnosti je průnikem doményT
přístupnosti s textemP
programu . - Pokud je deklarovaná přístupnost
M
private
, doménaM
přístupnosti je textT
programu .
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
M
vyhodnotí následující kroky, aby se zajistilo, že je přístup povolený:
- Za prvé, pokud
M
je 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
M
anopublic
, přístup je povolený.- V opačném případě
M
protected internal
je přístup povolen v případě, že dojde v rámci programu, ve kterémM
je deklarováno, nebo pokud dojde v rámci třídy odvozené od třídy, ve kteréM
je deklarováno a probíhá prostřednictvím odvozeného typu třídy (§7.5.4).- V opačném případě
M
protected
je přístup povolen v případě, že dojde v rámci třídy, ve kteréM
je deklarováno, nebo pokud dojde v rámci třídy odvozené od třídy, ve kteréM
je 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,
M
internal
pokud dojde v rámci programu, ve kterémM
je deklarován.- V opačném případě je přístup povolen,
M
private
pokud se vyskytuje v rámci typu, ve kterémM
je 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
A
přístupnosti aA.X
je neomezená.- Doména
A.Y
přístupnosti , ,B
B.X
,B.Y
B.C
, ,B.C.X
aB.C.Y
je programový text obsahující program.- Doména
A.Z
přístupnosti je textA
programu .- Doména
B.Z
přístupnosti aB.D
je textB
programu , včetně textuB.C
programu aB.D
.- Doména
B.C.Z
přístupnosti je textB.C
programu .- Doména
B.D.X
přístupnosti aB.D.Y
je textB
programu , včetně textuB.C
programu aB.D
.- Doména
B.D.Z
přístupnosti je textB.D
programu . 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.X
domény přístupnosti, které jsou omezené typem obsahujícího.end example
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
B
dědí soukromý členx
zA
třídy. Vzhledem k tomu, že člen je soukromý, je přístupný pouze v rámci class_bodyA
. Přístup kb.x
metoděA.F
tedy proběhne úspěšně, ale vB.F
metodě selže.end example
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.
Pojďme B
být základní třídou, která deklaruje člen M
chráněné instance , a nechat D
být třídou, která je odvozena z B
.
V rámci class_bodyD
může přístup k M
jedné z následujících forem:
- Nekvalifikovaný type_name nebo primary_expression formuláře
M
. - Primary_expression
D
- 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řistupovatx
prostřednictvím instancí obouA
aB
, protože v obou případech se přístup provádí prostřednictvím instanceA
nebo třídy odvozené zA
. V rámciB
však není možné přistupovatx
prostřednictvím instanceA
, protožeA
není odvozen zB
.end example
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
x
tři přiřazení, protože se provádějí prostřednictvím instancí typů tříd vytvořených z obecného typu.end example
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>.x
D
je platný, i když třídaD
je 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
B
třídy je chyba v době kompilace, protožeA
není alespoň tak přístupná jakoB
.end example
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
H
metodyB
je chyba v době kompilace, protože návratový typA
není alespoň tak přístupný jako metoda.end example
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,
params
modifikátory parametrů anithis
to, 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
params
modifiká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
params
modifiká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 in
modifikátory a out
ref
modifikátory parametrů jsou považovány za součást podpisu, členy deklarované v jednom typu se nemohou lišit pouze in
v podpisu , out
a 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í) in
out
a ref
jsou považovány za součást podpisu a vzájemně se neshodují.
Poznámka: Toto omezení je umožnit, aby programy jazyka C# byly snadno přeloženy tak, aby běžely v rozhraní příkazového řádku (Common Language Infrastructure), což neposkytuje způsob, jak definovat metody, které se liší pouze v
in
,out
aref
. 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 object
dynamic
, 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
in
modifikátory ,out
aref
parametru (§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
.out
ref
Všimněte si také, že návratový typ aparams
modifiká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. end example
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 jeN
nebo začínáN
teč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_clauses a 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_list interface_declaration (§18.2) je interface_base, type_parameter_constraints_clausea interface_body tohoto interface_declaration.
Rozsah parametru typu deklarovaného type_parameter_list delegate_declaration (§20.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 (§19.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.
Rozsah parametru deklarovaného v lambda_expression (§12.19) je lambda_expression_body tohoto lambda_expression.
Rozsah parametru deklarovaného v anonymous_method_expression (§12.19) 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
switch
prohlášení (§13.8.3) je switch_block.Rozsah místní proměnné deklarované v for_initializer
for
prohlášení (§13.9.4) je for_initializer, for_condition, for_iterator a embedded_statementfor
prohláš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é
F
odkazovat předi
deklarování.end example
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é
F
metodě první přiřazeníi
konkré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é.G
V metodě je použitíj
inicializátoru pro deklaracij
platné, protože použití před deklarátorem není.H
V 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.end example
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
A
se používá v kontextu výrazu odkazující na místní proměnnouA
a 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; } }
F
v rámci metody je proměnnái
instance skrytá místní proměnnoui
, ale v rámciG
metody stálei
odkazuje na proměnnou instance. Uvnitř místní funkceM1
float i
skryje okamžitě vnějšíi
. Parametri
lambda skryjefloat i
vnitřní část těla lambda.end example
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áF
deklarované vInner
, protože všechny vnější výskytyF
jsou skryty vnitřní deklarací. Z stejného důvodu má voláníF("Hello")
za následek chybu v době kompilace.end example
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ě nebo struktuře skryje všechny členy základní třídy se stejným názvem.
- Metoda zavedená ve třídě nebo struktuře 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ě nebo struktuře skryje všechny indexery základní třídy 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í
F
oDerived
příč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ší verzeBase
zavedlaF
metodu, která nebyla přítomna ve starší verzi třídy.end example
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() {} }
new
Modifikátor označuje, žeF
inDerived
je "nový", a že je skutečně určen ke skrytí zděděného člena.end example
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
F
inDerived
skryjeF
, která byla zděděna zBase
, ale vzhledem k tomu, že novýF
inDerived
má soukromý přístup, jeho rozsah se nevztahuje naMoreDerived
.F()
MoreDerived.G
Volání je tedy platné a vyvolá .Base.F
end example
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?
| namespace_or_type_name '.' identifier type_argument_list?
| qualified_alias_member
;
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.
Je-li namespace_or_type_namequalified_alias_member jeho význam, jak je popsáno v §14.8.1. V opačném případě má namespace_or_type_name jednu ze čtyř forem:
I
I<A₁, ..., Aₓ>
N.I
N.I<A₁, ..., Aₓ>
kde I
je jeden identifikátor, N
je namespace_or_type_name a <A₁, ..., Aₓ>
je volitelným type_argument_list. Pokud není zadán žádný type_argument_list , zvažte, že x
je nula.
Význam namespace_or_type_name se určuje takto:
- Je-li namespace_or_type_namequalified_alias_member, význam je uveden v §14.8.1.
- V opačném případě, pokud je namespace_or_type_name formuláře nebo
I
formulářeI<A₁, ..., Aₓ>
:- Pokud
x
je nula a namespace_or_type_name se zobrazí v deklaraci obecné metody (§15.6), ale mimo atributy hlavičky metody, a pokud tato deklarace obsahuje parametr typu (§15.2.3) s názvemI
, namespace_or_type_name odkazuje na tento parametr typu. - V opačném případě, pokud se namespace_or_type_name objeví v deklaraci typu, pak pro každý typ
T
instance (§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 nebo deklarace struktury (pokud existuje):- Pokud
x
je nula a deklaraceT
obsahuje parametr typu s názvemI
, namespace_or_type_name odkazuje na tento parametr typu. - Jinak pokud se namespace_or_type_name zobrazí v těle deklarace typu a
T
nebo některý z jeho základních typů obsahuje vnořený přístupný typ s parametry názvuI
ax
typu, pak namespace_or_type_name 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ů.
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
- Pokud
- 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
x
je nula aI
je název oboru názvů vN
, pak:- Pokud je umístění, kde dojde k namespace_or_type_name , uzavřeno deklarací
N
oboru názvů a deklarace oboru názvů obsahuje extern_alias_directive nebo using_alias_directive , které přidruží názevI
k oboru názvů nebo typu, je namespace_or_type_name nejednoznačný a dojde k chybě v době kompilace. -
V opačném případě namespace_or_type_name odkazuje na obor názvů pojmenovaný
I
vN
.
- Pokud je umístění, kde dojde k namespace_or_type_name , uzavřeno deklarací
- Jinak pokud
N
obsahuje přístupný typ s parametry názvuI
ax
typu, pak:- Pokud
x
je nula a umístění, kde dojde k namespace_or_type_name , je uzavřena deklarací oboru názvů aN
deklarace oboru názvů obsahuje extern_alias_directive nebo using_alias_directive , které přidruží názevI
k oboru názvů nebo typu, pak je namespace_or_type_name nejednoznačný a dojde k chybě kompilace. - V opačném případě namespace_or_type_name 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
x
je nula a deklarace oboru názvů obsahuje extern_alias_directive nebo using_alias_directive , která přidruží názevI
k importovanému oboru názvů nebo typu, pak namespace_or_type_name 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 názvem
I
ax
parametry typu, namespace_or_type_name odkazuje na tento typ vytvořený s danými argumenty typu. - V opačném případě, pokud obory názvů importované using_namespace_directivedeklarace oboru názvů obsahují více než jeden typ s názvem
I
ax
parametry typu, pak namespace_or_type_name je nejednoznačný a dojde k chybě.
- Pokud
- Pokud
- V opačném případě je namespace_or_type_name nedefinovaný a dojde k chybě v době kompilace.
- Pokud
-
V opačném případě je namespace_or_type_name formuláře
N.I
nebo formulářeN.I<A₁, ..., Aₓ>
.N
je nejprve vyřešen jako namespace_or_type_name. Pokud řešeníN
není úspěšné, dojde k chybě v době kompilace.N.I
Jinak neboN.I<A₁, ..., Aₓ>
se vyřeší následujícím způsobem:- Pokud
x
je nula aN
odkazuje na obor názvů aN
obsahuje vnořený obor názvů s názvemI
, namespace_or_type_name odkazuje na tento vnořený obor názvů. - V opačném případě pokud
N
odkazuje na obor názvů aN
obsahuje přístupný typ s parametry názvuI
ax
typu, namespace_or_type_name odkazuje na tento typ vytvořený s danými argumenty typu. - V opačném případě, pokud
N
odkazuje na (pravděpodobně sestavenou) třídu nebo typ struktury aN
nebo jakákoli z jejích základních tříd obsahují vnořený přístupný typ s názvemI
ax
parametry typu, pak namespace_or_type_name 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ů.Poznámka: Je-li význam
N.I
určen jako součást řešení specifikaceN
základní třídy, je považovánaN
za přímou základní tříduobject
(§15.2.4.2). koncová poznámka -
N.I
V opačném případě je neplatný namespace_or_type_name a dojde k chybě v době kompilace.
- Pokud
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
T
ve namespace_or_type_name formulářeT.I
, nebo -
namespace_or_type_name je
T
ve výrazu typu typeof (§12.8.18) ve tvarutypeof(T)
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
N
je členem globálního oboru názvů, jeho plně kvalifikovaný název jeN
. - V opačném případě je
S.N
jeho plně kvalifikovaný název , kdeS
je plně kvalifikovaný název oboru názvů nebo deklarace typu, ve kterémN
je 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<> } }
end example
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
A
a instanci třídyB
. Tyto objekty se stanou způsobilými pro uvolňování paměti, když je proměnnáb
př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 B
nebo
Finalize instance of B Finalize instance of A
vzhledem 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
A
B
, pak výstup tohoto programu může být:Finalize instance of A Finalize instance of B A.F RefA is not null
Všimněte si, že i když se instance
A
nepoužívala aA
finalizač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átoruB
způsobilo, že instanceA
, která se dříve nepoužívala, byla přístupná z živého odkazuTest.RefA
. Po voláníWaitForPendingFinalizers
je instanceB
oprávněna pro kolekci, ale instanceA
není, protože odkazTest.RefA
.end example
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