Sdílet prostřednictvím


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 je System.Threading.Tasks.Task nebo System.Threading.Tasks.Task<int>.
  • Návratový typ musí být void, int, System.Threading.Tasks.Tasknebo System.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. Metoda async void nebo async metoda vracející jiný typ awaitable, například ValueTask nebo ValueTask<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 Mainnávratový System.Threading.Tasks.Task typ 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 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 typem System.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 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

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.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í (§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 a Megacorp.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í a G metody chybu v době kompilace, protože název i je deklarován ve vnějším bloku a nelze ho předefinovat ve vnitřním bloku. Tyto H metody I jsou 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: Členy třídy object nejsou, přísně řečeno, členy jakéhokoli rozhraní (§18.4). Členy třídy object 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ýznam public je "přístup není omezen".
  • Chráněno, které je vybráno zahrnutím protected modifikátoru do deklarace členu. Intuitivní význam protected 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ýznam internal je "přístup omezený na toto sestavení".
  • Chráněná vnitřní, která je vybrána zahrnutím protected a modifikátoru internal v deklaraci členu. Intuitivní význam protected 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átoru protected i modifikátoru v deklaraci členu. Intuitivní význam private 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ýznam private 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 nebo internal 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 nebo internal deklarovanou přístupnost. koncová poznámka

  • Členy struktury můžou mít public, internalnebo private deklarovanou přístupnost a výchozí private nastavení pro deklarovanou přístupnost, protože struktury jsou implicitně zapečetěné. Členy struktury zavedené v struct (tj. neděděné tímto strukturou) nemohou mít protected, protected internalani private protected deklarované přístupnosti.

    Poznámka: Typ deklarovaný jako člen struktury může mít public, internalnebo private deklarovanou přístupnost, zatímco typ deklarovaný jako člen oboru názvů může mít pouze public nebo internal 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, 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 T veřejná, doména T přístupnosti je text P programu a jakýkoli program, který odkazuje P.
  • Pokud je deklarovaná přístupnost T interní, doména T přístupnosti je text Pprogramu .

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éna public přístupnosti je doména přístupnosti domény M.T
  • Pokud je deklarovaná přístupnost M , nechte protected internal být sjednocením textu D programu a text programu libovolného typu odvozeného z P, který je deklarován mimo T.P Doména M přístupnosti je průnikem domény TDpřístupnosti s .
  • Pokud je deklarovaná přístupnost M , nechte private protected být průnikem textu D programu a textu P programu a jakéhokoli typu odvozeného z T.T Doména M přístupnosti je průnikem domény TDpřístupnosti s .
  • Pokud je deklarovaná přístupnost M , nechte protected být sjednocením textu Dprogramu a text programu libovolného typu odvozeného z T.T Doména M přístupnosti je průnikem domény TDpřístupnosti s .
  • Pokud je deklarovaná přístupnost Minternal, doména M přístupnosti je průnikem domény T přístupnosti s textem Pprogramu .
  • Pokud je deklarovaná přístupnost Mprivate, doména M přístupnosti je text Tprogramu .

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 ano public, 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ém 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ě Mprotectedje 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, Minternalpokud dojde v rámci programu, ve kterém M je deklarován.
  • V opačném případě je přístup povolen, Mprivatepokud se vyskytuje v rámci typu, ve kterém M 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 a A.X je neomezená.
  • Doména A.Ypřístupnosti , , BB.X, B.YB.C, , B.C.Xa B.C.Y je programový text obsahující program.
  • Doména A.Z přístupnosti je text Aprogramu .
  • Doména B.Z přístupnosti a B.D je text Bprogramu , včetně textu B.C programu a B.D.
  • Doména B.C.Z přístupnosti je text B.Cprogramu .
  • Doména B.D.X přístupnosti a B.D.Y je text Bprogramu , včetně textu B.C programu a B.D.
  • Doména B.D.Z přístupnosti je text B.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šichni X členové veřejně deklarovanou přístupnost, všechny A.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ý člen x z A třídy. Vzhledem k tomu, že člen je soukromý, je přístupný pouze v rámci class_bodyA. Přístup k b.x metodě A.F tedy proběhne úspěšně, ale v B.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 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 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řistupovat x prostřednictvím instancí obou A a B, protože v obou případech se přístup provádí prostřednictvím instance A nebo třídy odvozené z A. V rámci Bvšak není možné přistupovat x prostřednictvím instance A, protože A není odvozen z B.

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 člena C<int>.xD je platný, i když třída D je odvozena od C<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že A není alespoň tak přístupná jako B.

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 metody B je chyba v době kompilace, protože návratový typ A 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ů ani this 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 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í 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, outa ref. 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 , outa ref parametru (§15.6.2) jsou součástí podpisu. Proto , F(int), F(in int), F(out int) a F(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ší pouze F(out int), F(ref int)a in.outref Všimněte si také, že návratový typ a params 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átoru params . Deklarace metod F(int) a F(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 je N nebo 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_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_initializerfor 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řed i 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 deklaraci j 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ěnnou A a v kontextu typu odkazující na třídu A.

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ěnnou i, ale v rámci G metody stále i odkazuje na proměnnou instance. Uvnitř místní funkce M1float i skryje okamžitě vnější i. Parametr i lambda skryje float 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é v Inner , protože všechny vnější výskyty F 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 o Derived 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ší verze Base zavedla F 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, že F in Derived 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 in Derived skryje F , která byla zděděna z Base, ale vzhledem k tomu, že nový F in Derived má soukromý přístup, jeho rozsah se nevztahuje na MoreDerived. 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áře I<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ázvem I, 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 deklarace T obsahuje parametr typu s názvem I, 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ázvu I a x 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

    • 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 a I je název oboru názvů v N, 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ázev I 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 v N.
      • Jinak pokud N obsahuje přístupný typ s parametry názvu I a x typu, pak:
        • Pokud x je nula a umístění, kde dojde k namespace_or_type_name , je uzavřena deklarací oboru názvů a N deklarace oboru názvů obsahuje extern_alias_directive nebo using_alias_directive , které přidruží název I 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.
      • 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ázev I 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 a x 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 a x parametry typu, pak namespace_or_type_name je nejednoznačný a dojde k chybě.
    • V opačném případě je namespace_or_type_name nedefinovaný a dojde k chybě v době kompilace.
  • V opačném případě je namespace_or_type_name formuláře N.I nebo formuláře N.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 nebo N.I<A₁, ..., Aₓ> se vyřeší následujícím způsobem:
    • Pokud x je nula a N odkazuje na obor názvů a N obsahuje vnořený obor názvů s názvem I, 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ů a N obsahuje přístupný typ s parametry názvu I a x 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 a N nebo jakákoli z jejích základních tříd obsahují vnořený přístupný typ s názvem I a x 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í specifikace N základní třídy, je považována N za přímou základní třídu object (§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.

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áře T.I, nebo
  • namespace_or_type_name je T ve výrazu typu typeof (§12.8.18) ve tvaru typeof(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 je N.
  • V opačném případě je S.Njeho plně kvalifikovaný název , kde S je plně kvalifikovaný název oboru názvů nebo deklarace typu, ve kterém N 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í:

  1. 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ý.
  2. 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

  3. 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í.
  4. 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

  5. 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řídy B. Tyto objekty se stanou způsobilými pro uvolňování paměti, když je proměnná b přiřazena hodnotu null, 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 AB, 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 a Afinalizační metoda byla spuštěna, je stále možné volat metody A (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átoru Bzpůsobilo, že instance A , která se dříve nepoužívala, byla přístupná z živého odkazu Test.RefA. Po volání WaitForPendingFinalizersje instance B oprávněna pro kolekci, ale instance A není, protože odkaz Test.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.