7 Základní koncepty

7.1 Spuštění a ukončení aplikace

7.1.1 Obecné

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.

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

Spuštění aplikace je zahájeno vyvoláním vstupního bodu vybraného v době kompilace z kandidátských vstupních bodů, které jsou uvedeny některým z následujících způsobů:

  1. Explicitně deklarováním metody s odpovídajícími vlastnostmi (§7.1.2).
  2. Implicitně pomocí příkazů nejvyšší úrovně (§7.1.3).
  3. Implementace je definována pomocí mechanismu vnějšího pro tuto specifikaci k určení vstupního bodu (§7.1.4).

Poznámka: S výjimkou kandidátů nejvyšší úrovně (§7.1.3) všechny vstupní body kandidáta jsou standardní metody; je kandidátem nebo vybraným vstupním bodem, nezabrání tomu, aby se tyto metody vyvolávaly jako normální. koncová poznámka

Pokud neexistují žádné vstupní body kandidáta, jedná se o chybu v době kompilace.

V opačném případě je vybrán jeden kandidát v době kompilace jako vstupní bod následující po procesu v §7.1.5.

Aplikace se spouští vyvoláním vybraného vstupního bodu, jak je popsáno v §7.1.6.

Stavový kód ukončení, jehož účelem je umožnit komunikaci úspěchu nebo selhání hostitelského prostředí, je určen na základě výsledku vyvolání vstupního bodu (§7.1.6).

7.1.2 Pojmenované vstupní body

Metoda se kvalifikuje jako vstupní bod canditate 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ý.
  • Návratový typ musí být void, int, System.Threading.Tasks.Tasknebo System.Threading.Tasks.Task<int>.
  • Modifikátor může mít async pouze v případě, že návratový typ metody je System.Threading.Tasks.Task nebo System.Threading.Tasks.Task<int>.
  • Nejedná se o volitelnou částečnou metodu (§15.6.9) bez prováděcího prohlášení.
  • Seznam parametrů musí být prázdný nebo musí mít jeden parametr hodnoty typu string[].

Deklarovaná přístupnost (§7.4.2) metody je ignorována pro účely kvalifikace jako vstupního bodu kandidáta. Při spuštění aplikace se vyvolá vybraný vstupní bod bez ohledu na deklarovanou přístupnost. Pokud se však vybraný vstupní bod kandidáta vyvolá po spuštění aplikace, bude i nadále platit jakákoli deklarovaná přístupnost.

Poznámka: Zde uvedené požadavky nezadávají, že Main by měl být členem třídy, může to být člen struktury. koncová poznámka

7.1.3 Použití příkazů nejvyšší úrovně

Jedna kompilační jednotka (§14.2 v aplikaci může obsahovat statement_list; označované jako příkazy nejvyšší úrovně. Význam příkazů nejvyšší úrovně je sémanticky ekvivalentní deklarování následujícího v globálním oboru názvů:

partial class Program
{
    static «AsyncAndReturnType» «Main»(string[] args)
    {
        «statement_list»
    }
}

Třída Program je částečná deklarace typu (§15.2.7), která se kombinuje s jinými částečnými deklaracemi třídy pro Program v rámci žádosti.

Poznámka: To znamená, že příkaz nejvyšší úrovně má stejná práva přístupu jako příkaz v těle statické metody definované ve třídě Program v globálním oboru názvů. koncová poznámka

Název «Main» metody je zástupný symbol pro nezadanou zadanou implementaci zadaný název, který je přímo přístupný pouze pro spouštěcí prostředí a nelze na něho odkazovat přímo z aplikace. Dále není nutné, aby použitý název byl platný jako název metody jazyka C#.

Poznámka: Vyjádření použitého názvu lze však získat jako string během provádění příkazu nejvyšší úrovně, pokud vyvolá metodu, která používá CallerMemberName atribut (§23.5.6.4). koncová poznámka

Jedná se «statement_list» o zástupný symbol pro statement_listkompilační jednotky.

args Parametr je v oboru v rámci příkazů nejvyšší úrovně, nikoli v opačném případě. Pravidla konfliktu nebo stínování regulárních názvů platí. Tento parametr obdrží parametry aplikace (§7.1.6).

Asynchronní operace jsou povoleny v příkazech nejvyšší úrovně ve stupních, ve které jsou povoleny v příkazech v rámci pojmenované metody async entry-point.

Nahrazení zástupného symbolu «AsyncAndReturnType» v podpisu metody je určeno na základě obsahu příkazů nejvyšší úrovně následujícím způsobem:

Příkazy nejvyšší úrovně obsahují Vygenerovaný podpis
Ne await nebo return s hodnotou static void «Main»(string[] args)
return pouze s hodnotou static int «Main»(string[] args)
await pouze static async Task «Main»(string[] args)
await a return s hodnotou static async Task<int> «Main»(string[] args)

Tyto vygenerované podpisy splňují požadavky na pojmenované vstupní body (§7.1.2), a proto «Main» jsou kandidátským vstupním bodem.

Poznámka: Tyto podpisy neobsahují deklarovanou přístupnost (§7.4.2). Implementace však může zahrnovat deklarovanou přístupnost – protože přístupnost je ignorována pro metody vstupního bodu (§7.1.2), metoda by stále byla sémanticky ekvivalentní.

7.1.4 Externě definovaný vstupní bod

Implementace může poskytnout mechanismus externí pro tuto specifikaci, který určuje vstupní bod kandidáta.

Pokud tento mechanismus identifikuje typ v rámci programu, určí se vstupní bod kandidáta určený pro pojmenované vstupní body (§7.1.2).

Pokud tento mechanismus identifikuje metodu v rámci programu, může mít metoda libovolný název, ale jinak musí splňovat požadavky stanovené pro pojmenované vstupní body (§7.1.2).

Externí mechanismus může zadat pouze jeden vstupní bod kandidáta.

Vstupní bod kandidáta určený externím mechanismem má přednost ve výběru vstupního bodu (§7.1.5).

7.1.5 Výběr vstupního bodu

Pokud neexistují žádné vstupní body kandidáta, jedná se o chybu v době kompilace.

V opačném případě je vstupní bod vybrán z kandidátů:

  • Je-li některý z kandidátů určen externím mechanismem (§7.1.4), je vybrán jako vstupní bod;
  • V opačném případě je jeden z kandidátů definován příkazy nejvyšší úrovně (§7.1.3) je vybrán jako vstupní bod;
  • Jinak:
    • Pokud některý z kandidátů má návratový int typ nebo void pak některý z kandidátů, kteří mají návratový typ System.Threading.Tasks.Task nebo System.Threading.Tasks.Task<int> jsou z fondu kandidátů odebráni.
    • Pokud existuje jeden zbývající kandidát, vybere se jako vstupní bod.
    • Jinak nelze určit vstupní bod a zobrazí se chyba v době kompilace.

Vstupní bod vybraný v době kompilace je vyvolán za běhu jako součást spuštění aplikace (§7.1.6).

7.1.6 Vyvolání vstupního bodu

Pokud vstupní bod deklaruje parametr, pak implementace jako počáteční hodnota tohoto parametru poskytne nenulový odkaz na pole řetězců. Toto pole se skládá z nenulových odkazů na nula nebo více řetězců označovaných jako parametry aplikace, které jsou před spuštěním aplikace dány hodnotami definovanými implementací hostitelského 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

Proces spuštění a ukončení aplikace je sémanticky ekvivalentní následujícím krokům:

  • Spuštění aplikace je spuštěno buď:
    • Vyvolání metody vstupního bodu (§12.8.10), pokud je void jeho návratový typ nebo ; nebo int
    • Čekání (§12.9.9) na výsledek vyvolání metody vstupního bodu, pokud je jeho návratovým typem Task typ.
    • V obou případech, pokud vstupní bod vyžaduje argument, je pole parametrů aplikace zadáno jako jeho hodnota.

Poznámka: Vyvolání metody vstupního bodu způsobí provedení statického konstruktoru, pokud existuje, ohraničujícího typu jako první (§15.12, §16.8.10). koncová poznámka

  • Aplikace se ukončí.
    • Pokud spuštění vede k int hodnotě, která slouží jako stavový kód ukončení;
    • V opačném případě, pokud spuštění vede k žádné návratové hodnotě stavový kód ukončení je 0;
    • Jinak platí, že pokud se spuštění ukončí z důvodu výjimky (§22.4), je výstupní kód definován implementací. Kromě toho může implementace poskytovat alternativní rozhraní API pro zadání ukončovacího kódu.

Zda jsou finalizátory (§15.13) spuštěny jako součást ukončení aplikace, je definována implementace.

Note: Implementace architektury .NET 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, pokud takové vyčištění nebylo potlačeno (například voláním metody knihovny GC.SuppressFinalize). koncová poznámka

7.2 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.8) slouží k definování tříd (§15), struktur (§16), rozhraní (§19), výčtů (§20) a delegátů (§21). Typy členů povolených v deklaraci typu závisí na podobě deklarace typu. Například: Deklarace třídy mohou obsahovat deklarace konstant (§15.4), pole (§15.5), metody (§15.6), vlastnosti (§15.7), události (§15.8), indexery (§15.9) ), operátory (§15.10), konstruktory instancí (§15.11), statické konstruktory (§15.12), finalizátory (§15.13) a vnořené typy (§15.3.9).

Deklarace definuje název v prostoru deklarace, do kterého deklarace patří. Jedná se o chybu v době kompilace, která obsahuje dvě nebo více deklarací, které zavádějí členy se stejným názvem v prostoru deklarací s výjimkou následujících případů:

  • Dvě nebo více deklarací oboru názvů se stejným názvem jsou povoleny ve stejném prostoru deklarace. Tyto deklarace oboru názvů jsou agregovány tak, aby vytvořily jeden logický obor názvů a sdílely jediný prostor deklarace.
  • Deklarace v samostatných programech, ale ve stejném prostoru deklarace oboru názvů mohou sdílet stejný název.

    Poznámka: Tyto deklarace však mohou v případě zahrnutí do stejné aplikace zavést nejednoznačnosti. koncová poznámka

  • Dvě nebo více metod se stejným názvem, ale odlišné podpisy jsou povoleny ve stejném prostoru deklarace (§7.5).
  • 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.7.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.7.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. Podle §14.3 to zahrnuje file_scoped_namespace_declarations.
  • 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 pro deklaraci aliasu (§14.6.2).
  • Každá nečástečně třídou, strukturou nebo deklarací rozhraní vytvoří nový prostor deklarace. Každá částečná třída, struktura nebo deklarace rozhraní přispívá k prostoru deklarace sdílenému všemi odpovídajícími částmi ve stejném programu (§16.2.4). Názvy se do tohoto prostoru deklarace zavádějí prostřednictvím class_member_declarations, struct_member_declarations, interface_member_declarations nebo type_parameters. S výjimkou přetížených deklarací konstruktoru instance a deklarací statického konstruktoru nemůže třída, struktura nebo rozhraní obsahovat deklaraci člena se stejným názvem jako třída, struktura nebo rozhraní. Třída, struktura nebo rozhraní umožňují deklaraci přetížených metod a indexerů. Třída nebo struktura navíc umožňuje deklaraci přetížených konstruktorů a operátorů instance. Například třída, struktura nebo rozhraní mohou obsahovat více deklarací metody se stejným názvem za předpokladu, že se tyto deklarace metody liší v podpisu (§7.5). 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_collections) 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_collections) a type_parameters. Objekt set a init 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 rozsahu vnořeného místního názvu je vnější místní název skrytý (§7.6.1) podle vnořeného místního názvu.

    Poznámka: Zahoďte parametry anonymních funkcí (§12.22.2) nezavádějí názvy do žádného prostoru deklarace. koncová poznámka

  • 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.23.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.6).
  • Pořadí prohlášení pro deklarace členů výčtu (§20.4) je významné, pokud jsou vynechány constant_expression hodnoty.

Příklad: Prostor deklarace oboru názvů je otevřený a dvě deklarace oboru názvů se stejným plně kvalifikovaným názvem přispívají do stejného prostoru deklarace. Například

namespace Megacorp.Data
{
    class Customer
    {
        ...
    }
}

namespace Megacorp.Data
{
    class Order
    {
        ...
    }
}

Dvě deklarace oboru názvů výše přispívají ke stejnému prostoru deklarace, v tomto případě deklarují dvě třídy s plně kvalifikovanými názvy Megacorp.Data.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.

konec příkladu

Poznámka: Jak je uvedeno výše, deklarací prostoru bloku zahrnuje všechny vnořené bloky. Proto v následujícím příkladu 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.3 Členové

7.3.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.4.2). koncová poznámka

7.3.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.3.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.3.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.3.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.3.6 Členy rozhraní

Členy rozhraní jsou členy deklarované v rozhraní a ve všech základních rozhraních rozhraní.

Poznámka: Členové třídy object nejsou, přísně řečeno, členy jakéhokoli rozhraní (§19.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.3.7 Maticové členy

Členy pole jsou členy zděděné z třídy System.Array.

7.3.8 Delegáti členové

Delegát dědí členy z třídy System.Delegate. Kromě toho obsahuje metodu s Invoke názvem se stejným návratovým typem a seznamem parametrů zadaným v prohlášení (§21.2). Vyvolání této metody se chová stejně jako vyvolání delegáta (§21.6) na stejné instanci delegáta.

Implementace může poskytovat další členy, a to buď prostřednictvím dědičnosti, nebo přímo v samotném delegátu.

7.4 Přístup členů

7.4.1 Obecné

Deklarace členů umožňují kontrolu nad přístupem členů. Přístupnost člena je stanovena deklarovanou přístupností (§7.4.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.4.3) člena.

7.4.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 rozhraní, nebo třídy nebo rozhraní odvozené z typu".
  • 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.
  • Členové výčtu implicitně deklarovali public přístupnost. U deklarací členů výčtu nejsou povoleny žádné modifikátory přístupu.

7.4.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, pokud dojde v rámci programu, ve kterém M je deklarován, nebo pokud se vyskytuje v rámci třídy odvozené od třídy, ve které M je deklarován a probíhá prostřednictvím odvozeného typu třídy (§7.4.4).
  • V opačném případě Mprotectedje přístup povolen, pokud se vyskytuje v rámci třídy, ve které M je deklarováno, nebo pokud se vyskytuje v rámci třídy odvozené od třídy, ve které M je deklarován a probíhá prostřednictvím odvozeného typu třídy (§7.4.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.

konec příkladu

Jak je popsáno v §7.3, 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.

konec příkladu

7.4.4 Chráněný přístup

protected private protected Je-li člen instance přístupný mimo text programu třídy, ve které je deklarován, a když protected internal je člen instance přístupný mimo text programu programu, ve kterém je deklarován, bude přístup probíhat v deklaraci třídy, která je odvozena od třídy, ve které je deklarována. Kromě toho se vyžaduje přístup prostřednictvím instance tohoto odvozeného typu třídy nebo typu třídy vytvořeného z něj. Toto omezení zabraňuje, aby jedna odvozená třída přistupovala k chráněným členům jiných odvozených tříd, i když jsou členy zděděné ze stejné základní třídy. K členům rozhraní instance definovaným s protected přístupem nebo private protected k přístupu nelze získat přístup z class rozhraní nebo struct které implementuje toto rozhraní. K těmto rozhraním lze přistupovat pouze z odvozených rozhraní. class Typy struct však mohou implementovat protected členy instance deklarované v rozhraní, které implementují.

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. Přístup k class_body (nebo D) Mmůže mít jeden z následujících formulářů:

  • Nekvalifikovaný type_name nebo primary_expression formuláře M.
  • Primary_expression
  • Primary_expression formuláře base.M.
  • Primary_expression formuláře base[argument_list].

Kromě těchto forem přístupu může odvozená třída přistupovat k konstruktoru chráněné instance základní třídy v constructor_initializer (§15.11.2).

Příklad: V následujícím kódu

public class A
{
    protected int x;

    static void F(A a, B b)
    {
        a.x = 1; // Ok
        b.x = 1; // Ok
    }
}

public class B : A
{
    static void F(A a, B b)
    {
        a.x = 1; // Error, must access through instance of B
        b.x = 1; // Ok
    }
}

v rámci A, je možné př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.

konec příkladu

Příklad:

class C<T>
{
    protected T x;
}

class D<T> : C<T>
{
    static void F()
    {
        D<T> dt = new D<T>();
        D<int> di = new D<int>();
        D<string> ds = new D<string>();
        dt.x = default(T);
        di.x = 123;
        ds.x = "test";
    }
}

V tomto případě jsou povolena 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.

konec příkladu

Poznámka: Doména přístupnosti (§7.4.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.4.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.

konec příkladu

Příklad: Podobně v následujícím kódu

class A {...}

public class B
{
    A F() {...}
    internal A G() {...}
    public A H() {...}
}

Výsledkem H metody B je chyba v době kompilace, protože návratový typ A není alespoň tak přístupný jako metoda.

konec příkladu

7.5 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ě nezahrnuje návratový typ, názvy parametrů, názvy parametrů, názvy parametrů typu, omezení parametru typu, paramsmodifikátory , scopednebo this parametry, ani 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 scoped modifikátor, ani neobsahuje params modifikátor, který může být zadán pro parametr nejvíce vpravo, nebo scoped modifikátor, 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 neobsahuje konkrétně typ výsledku scoped ani modifikátor.
  • 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 ani není scoped modifikátor.
  • 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é.

I když parameter_mode_modifierjsou považovány za součást podpisu, členové deklarované v jednom typu se nemohou v podpisu lišit pouze těmito modifikátory. 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í) se parameter_mode_modifierpovažují za součást podpisu a vzájemně se neshodují.

Poznámka: Toto omezení spočívá v tom, že programy jazyka C# lze snadno přeložit tak, aby běžely na platformě, která neposkytuje způsob, jak definovat metody, které se liší pouze v jejich parameter_mode_modifiers. 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(ref readonly int x); // F(ref int) error
    void F(out int x);          // F(out int) error
    void F(object o);           // F(object)
    void F(dynamic d);          // error.
    void F(int x, int y);       // F(int, int)
    int F(string s);            // F(string)
    int F(int x);               // F(int) error
    void F(string[] a);         // F(string[])
    void F(params string[] a);  // F(string[]) error
    void F<S>(S s);             // F<0>(0)
    void F<T>(T t);             // F<0>(0) error
    void F<S,T>(S s);           // F<0,1>(0)
    void F<T,S>(S s);           // F<0,1>(1) ok
}

Všimněte si, že všechny parameter_mode_modifiers (§15.6.2) jsou součástí podpisu. Proto , F(int), F(in int), F(out int) , F(ref int)a F(ref readonly int) jsou všechny jedinečné podpisy. Však , , F(out int)F(ref int)a nelze F(ref readonly int) deklarovat v rámci stejného rozhraní, F(in int)protože jejich podpisy se liší pouze jejich parameter_mode_modifiers. 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. konec příkladu

7.6 Obory

7.6.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.2 , které 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.

Poznámka: Není možné získat přístup ke skrytému vnějšímu názvu z vnitřního oboru, protože neexistuje způsob jeho kvalifikace, například parametry typu u vnořených deklarací typu. koncová poznámka

  • Rozsah člena oboru názvů deklarovaného namespace_member_declaration (§14.7) bez uzavření namespace_declaration je celý text programu.
  • Rozsah člena oboru názvů deklarovaného compilation_unit_body , jehož plně kvalifikovaný název je N, je každý compilation_unit_body , jehož plně kvalifikovaný název je N nebo začíná Ntečkou.
  • 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. Podle §14.3 se file_scoped_namespace_declarations považují za ekvivalentní namespace_declarations pro toto pravidlo.
  • Rozsah názvu definovaného extern_alias_directive (§14.4) se vztahuje na global_using_directives, using_directives, global_attributes a compilation_unit_body jeho bezprostředně obsahujícího 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, namespace_body a file_scoped_namespace_declaration , ve kterých k němu dochází.
  • Rozsah názvu definovaného nebo importovaného global_using_directive přesahuje global_attributes a compilation_unit_body všech compilation_unitv programu.
  • Rozsah názvu definovaného nebo importovaného using_directive (§14.6) se vztahuje na global_attributes, statement_lista namespace_member_declarationcompilation_unit nebonamespace_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_listclass_declaration (§15.2) je class_base, type_parameter_constraints_clausea class_body (nebo record_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 (nebo record_struct_body) tohoto struct_declaration.
  • Rozsah parametru typu deklarovaného type_parameter_listinterface_declaration (§19.2) je interface_base, type_parameter_constraints_clauses a interface_body tohoto interface_declaration.
  • Rozsah parametru typu deklarovaného type_parameter_list na delegate_declaration (§21.2) je return_type, parameter_list a type_parameter_constraints_clausedelegate_declaration.
  • Rozsah parametru typu deklarovaného type_parameter_list na method_declaration (§15.6.1) je method_declaration a nameof výrazy v atributu method_declaration nebo jeho parametry.
  • Rozsahem člena deklarovaného class_member_declaration (§15.3.1) je class_body (nebo record_class_body), ve kterém k prohlášení dochází. Kromě toho rozsah člena třídy rozšiřuje class_body (nebo record_class_body) odvozených tříd, které jsou zahrnuty do domény přístupnosti (§7.4.3) člena.
  • Rozsahem člena deklarovaného struct_member_declaration (§16.3) je struct_body (nebo record_struct_body), ve kterém k prohlášení dochází.
  • Rozsahem člena deklarovaného enum_member_declaration (§20.4) je enum_body , ve kterém k prohlášení dochází.
  • Rozsah parametru deklarovaného v method_declaration (§15.6) je method_body nebo ref_method_body tohoto method_declaration a nameof výrazů v atributu deklarace metody nebo jeho parametrů.
  • 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.
  • S výjimkou parametrů zahození (§12.22.2) je rozsah parametru deklarovaného v lambda_expression (§12.22) lambda_expression_body tohoto lambda_expression a nameof výrazů v atributu anonymní funkce nebo jeho parametrů.
  • S výjimkou parametrů zahození (§12.22.2) je rozsah parametru deklarovaného v anonymous_method_expression (§12.22) blokem tohoto anonymous_method_expression a nameof výrazů v atributu anonymní funkce nebo jeho parametrů.
  • Rozsah označení deklarovaného v labeled_statement (§13.5) je blok , ve kterém se prohlášení vyskytuje.
  • Rozsah místní proměnné deklarované v local_variable_declaration (§13.6.2) je blok , ve kterém se deklarace vyskytuje.
  • Rozsah místní proměnné deklarované v prohlášení (switch) je switch_block.
  • Rozsah místní proměnné deklarované v for_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í.

konec příkladu

V rámci oboru místní proměnné se jedná o chybu v době kompilace odkazující na místní proměnnou v textové pozici, která předchází jeho deklarátoru.

Příklad:

class A
{
    int i = 0;

    void F()
    {
        i = 1;                // Error, use precedes declaration
        int i;
        i = 2;
    }

    void G()
    {
        int j = (j = 1);     // Valid
    }

    void H()
    {
        int a = 1, b = ++a; // Valid
    }
}

Ve výše uvedené 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.

konec příkladu

Poznámka: Pravidla oborů pro místní proměnné a místní konstanty jsou navržena tak, aby zaručit, že význam názvu použitého v kontextu výrazu je vždy stejný v rámci bloku. Pokud by rozsah místní proměnné byl rozšířen pouze z jeho deklarace na konec bloku, pak v předchozím příkladu by první přiřazení přiřadilo proměnné instance a druhé přiřazení by přiřadilo místní proměnné, což by mohlo vést k chybám v době kompilace, pokud by příkazy bloku byly později přeuspořádané.)

Význam názvu v bloku se může lišit v závislosti na kontextu, ve kterém se název používá. V příkladu

class A {}

class Test
{
    static void Main()
    {
        string A = "hello, world";
        string s = A;                      // expression context
        Type t = typeof(A);                // type context
        Console.WriteLine(s);              // writes "hello, world"
        Console.WriteLine(t);              // writes "A"
    }
}

název 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

Jak je popsáno v §7.1.3, zdrojové tokeny nejvyšší úrovně jsou uzavřeny vygenerovanou metodou vstupního bodu.

Pro účely vyhodnocení jednoduchého názvu se nejprve po dosažení globálního oboru názvů provede pokus o vyhodnocení názvu v rámci vygenerované metody vstupního bodu a pouze v případě, že tento pokus selže, je vyhodnocení v rámci globální deklarace oboru názvů provedeno.

To může vést ke stínování názvů a typů deklarovaných v rámci globálního oboru názvů a stínování importovaných názvů.

Pokud k vyhodnocení jednoduchého názvu dojde mimo příkazy nejvyšší úrovně a vyhodnocení vrátí místní proměnnou nebo funkci nejvyšší úrovně, výsledkem chyby v době kompilace.

7.6.2 Název skrytí

7.6.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.6.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.

konec příkladu

Když název ve vnitřním oboru skryje název ve vnějším oboru, skryje všechny přetížené výskyty tohoto názvu.

Příklad: V následujícím kódu

class Outer
{
    static void F(int i) {}
    static void F(string s) {}

    class Inner
    {
        static void F(long l) {}

        void G()
        {
            F(1); // Invokes Outer.Inner.F
            F("Hello"); // Error
        }
    }
}

volání F(1) vyvolá 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.

konec příkladu

7.6.2.3 Skrytí prostřednictvím dědičnosti

K skrytí názvů prostřednictvím dědičnosti dochází, když třídy nebo struktury znovu předefinují názvy, které byly zděděny ze základních tříd. Tento typ skrývání názvů má jeden z následujících formulářů:

  • Konstanta, pole, vlastnost, událost nebo typ zavedený ve třídě, struktuře nebo rozhraní skryje všechny členy základní třídy se stejným názvem.
  • Metoda zavedená ve třídě, struktuře nebo rozhraní skryje všechny členy základní třídy bez metody se stejným názvem a všechny metody základní třídy se stejným podpisem (§7.5).
  • Indexer zavedený ve třídě, struktuře nebo rozhraní skryje všechny indexery základního typu se stejným podpisem (§7.5).

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 přijít, protože novější verze Base zavedla metodu F , která nebyla přítomna ve starší verzi třídy.

konec příkladu

Upozornění způsobené skrytím zděděného názvu lze odstranit pomocí modifikátoru new :

Příklad:

class Base
{
    public void F() {}
}

class Derived : Base
{
    public new void F() {}
}

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.

konec příkladu

Deklarace nového členu skryje zděděný člen pouze v rámci rozsahu nového člena.

Příklad:

class Base
{
    public static void F() {}
}

class Derived : Base
{
    private new static void F() {} // Hides Base.F in Derived only
}

class MoreDerived : Derived
{
    static void G()
    {
        F();                       // Invokes Base.F
    }
}

V předchozím příkladu deklarace 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

konec příkladu

7.7 Názvy oborů názvů a typů

7.7.1 Obecné

Několik kontextů v programu jazyka C# vyžaduje zadání namespace_name nebo type_name .

namespace_name
    : namespace_or_type_name
    ;

type_name
    : namespace_or_type_name
    ;

namespace_or_type_name
    : identifier type_argument_list? ('.' identifier type_argument_list?)*
    | qualified_alias_member ('.' identifier type_argument_list?)*
    ;

Namespace_name je namespace_or_type_name, která odkazuje na obor názvů.

V případě řešení, jak je popsáno níže, namespace_or_type_namenamespace_name odkazuje na obor názvů nebo jinak dojde k chybě v době kompilace. V namespace_name nemohou být přítomny žádné argumenty typu (§8.4.2) (argumenty typu mohou mít pouze typy).

Type_name je namespace_or_type_name, která odkazuje na typ.

V případě řešení, jak je popsáno níže, namespace_or_type_nametype_name odkazuje na typ nebo jinak dojde k chybě v době kompilace.

Namespace_or_type_name odkazuje na typ nebo obor názvů. Řešení konkrétního oboru názvů nebo typu zahrnuje dva kroky založené na rozdělení gramatiky na úvodní část, což je jeden z fragmentů gramatiky:

  • identifier type_argument_list?
  • qualified_alias_member

a koncovou část, která je fragmentem gramatiky:

  • ('.' identifier type_argument_list?)*

Nejdříve se řeší úvodní část k určení R₀, počátečního oboru názvů nebo typu.

Je-li úvodní část namespace_or_type_namequalified_alias_member, pak R₀ je obor názvů nebo typ identifikovaný tím, že tento typ je popsán v §14.9.1.

V opačném případě bude úvodní část, která je fragmentem gramatiky identifier type_argument_list?, mít jednu z následujících forem:

  • I
  • I<A₁, ..., Aₓ>

kde:

  • I je jeden identifikátor; a
  • <A₁, ..., Aₓ> je type_argument_list, pokud není zadaná žádná type_argument_list , zvažte x hodnotu nula.

R₀ je určen takto:

  • Pokud x je nula a namespace_or_type_name se zobrazí v deklaraci obecné metody (§15.6), ale mimo atributy hlavičkymetody, a pokud tato deklarace obsahuje parametr typu (§15.2.3) s názvem I, odkazuje R₀ na tento parametr typu.
  • V opačném případě pokud se namespace_or_type_name zobrazí v deklaraci typu, pak pro každý typ 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, struktury nebo deklarace rozhraní (pokud existuje):
    • Pokud x je nula a deklarace T obsahuje parametr typu s názvem I, odkazuje R₀ na tento parametr typu.
    • V opačném případě, pokud se namespace_or_type_name objeví v těle deklarace typu a pokud T nebo některý z jeho základních typů obsahuje vnořený přístupný typ s názvem I a parametry typu x, pak R₀ odkazuje na tento typ vytvořený s danými argumenty typu. Pokud existuje více než jeden takový typ, je vybrán typ deklarovaný v rámci více odvozených typů. Jedná se o chybu kompilátoru, pokud žádný typ není odvozenější než všechny ostatní.

      Poznámka: Netypové členy (konstanty, pole, metody, vlastnosti, indexery, operátory, konstruktory instancí, finalizátory a statické konstruktory) a členy typu s jiným počtem parametrů typu se při určování významu namespace_or_type_name ignorují. koncová poznámka

    • Jinak platí, že pro každý obor názvů počínaje oborem názvů N, ve kterém dojde k namespace_or_type_name , pokračujeme s každým uzavřeným oborem názvů (pokud existuje) a končíme globálním oborem názvů, následující kroky se vyhodnotí, dokud se neskončí entita:
      • Pokud x je nula a I je název oboru názvů v N, pak:
        • Pokud je umístění, ve kterém 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, nebo jakákoli deklarace oboru názvů v N programu obsahuje global_using_alias_directive , který přidruží název I k oboru názvů nebo typu, namespace_or_type_name je nejednoznačný a dojde k chybě v době kompilace.
        • R₀ V opačném případě 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, nebo jakákoli deklarace oboru názvů v N programu obsahuje global_using_alias_directive , který přidruží název I k oboru názvů nebo typu, namespace_or_type_name je nejednoznačný a dojde k chybě v době kompilace.
        • R₀ V opačném případě 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, nebo jakákoli deklarace oboru názvů v N programu obsahuje global_using_alias_directive , která přidruží název I k importovanému oboru názvů nebo typu, odkazuje R₀ na tento obor názvů nebo typ.
        • Jinak pokud obory názvů importované using_namespace_directivedeklarace oboru názvů a obory názvů a deklarace typů importované global_using_namespace_directives a global_using_static_directivejakékoli deklarace oboru názvů v N programu obsahují přesně jeden typ s parametry názvu I a x typu, pak R₀ odkazuje na tento typ vytvořený s danými argumenty typu.
        • Jinak pokud obory názvů importované using_namespace_directivedeklarace oboru názvů a obory názvů a deklarace typů importované global_using_namespace_directives a global_using_static_directivejakékoli deklarace oboru názvů v N programu obsahují více než jeden typ s parametry názvu I a x typu, pak je namespace_or_type_name nejednoznačný a dojde k chybě kompilace.
    • V opačném případě je namespace_or_type_name nedefinovaný a dojde k chybě v době kompilace.

Pokud bylo R₀ vyřešeno, poté je vyřešena koncová část namespace_or_type_name. Koncový gramatický fragment se skládá z k ≥ 0 opakování, přičemž každé opakování dále rozlišuje odkazovaný obor názvů nebo typ.

Pokud k je nula, tj. neexistuje žádná koncová část, pak se namespace_or_type_name přeloží na R₀.

V opačném případě bude mít každé opakování jednu z těchto forem:

  • .I
  • .I<A₁, ..., Aₓ>

kde I, A a x jsou definovány jako výše.

Pro každé opakování n, kde 1 ≤ n ≤ k, jeho rozlišení Rₙ, které zahrnuje Rₚ, kde p = n - 1, rozlišení předchozího opakování, je určeno takto:

  • Pokud x je nula a Rₚ odkazuje na obor názvů a Rₚ obsahuje vnořený obor názvů s názvem I, Rₙ odkazuje na tento vnořený obor názvů.
  • Rₚ Pokud se v opačném případě odkazuje na obor názvů a Rₚ obsahuje přístupný typ s parametry názvu I a x typu, Rₙ odkazuje na tento typ vytvořený s danými argumenty typu.
  • V opačném případě, pokud Rₚ odkazuje na (pravděpodobně konstruovanou) třídu, strukturu nebo typ rozhraní a Rₚ nebo jakýkoli z jeho základních typů obsahují vnořený přístupný typ s názvem I a x parametry typu, pak Rₙ odkazuje na tento typ vytvořený s danými argumenty typu. Pokud existuje více než jeden takový typ, je vybrán typ deklarovaný v rámci více odvozených typů. Jedná se o chybu kompilátoru, pokud žádný typ není odvozenější než všechny ostatní.

    Poznámka: Je-li význam pro T.Iurčitý typ Turčen jako součást řešení specifikace T základní třídy, je považována T za přímou základní třídu object (§15.2.4.2). koncová poznámka

  • V opačném případě je namespace_or_type_name neplatný a dojde k chybě v době kompilace.

Řešení namespace_or_type_name je řešení konečného opakováníRₖ.

Namespace_or_type_name je povoleno odkazovat na statickou třídu (§15.2.2.4) pouze v případě, že

  • Namespace_or_type_name je 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)

Příklad:

interface A
{
    class NestedClass { public static void M() {} }
}

interface B
{
    class NestedClass { public static void M() {} }
}

interface C : A, B
{
    public void Test() { NestedClass.M(); } // ambiguity between
                                            // A.NestedClass and B.NestedClass
}

V předchozím příkladu je volání NestedClass.M() in C.Test() nejednoznačné mezi B.NestedClass.M() a A.NestedClass.M() protože ani jeden z nich není odvozenější než druhý. Explicitní odkaz na buď A.NestedClass.M() nebo B.NestedClass.M() je nutný k vyřešení nejednoznačnosti.

konec příkladu

7.7.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.7.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<>
    }
}

konec příkladu

7.8 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.1).

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.

konec příkladu

7.9 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.