Megosztás a következőn keresztül:


7 Alapfogalmak

7.1 Alkalmazás indítása

A program fordítható osztálytárként, amelyet más alkalmazások részeként, vagy közvetlenül elindítható alkalmazásként lehet használni. Az összeállítási mód meghatározásának mechanizmusa implementáció által definiált és ezen specifikáción kívül esik.

Egy alkalmazásként összeállított programnak tartalmaznia kell legalább egy belépési pontnak minősülő módszert a következő követelmények teljesítésével:

  • Ennek a névvel Mainkell rendelkeznie.
  • A rendeletnek tartalmaznia statickell.
  • Ez nem lehet általános.
  • Nem általános típusban kell deklarálni. Ha a metódust deklaráló típus beágyazott típus, a beágyazási típusok egyike sem lehet általános.
  • Lehet, hogy rendelkezik a async módosítóval, feltéve, hogy a metódus visszatérési típusa vagy System.Threading.Tasks.TaskSystem.Threading.Tasks.Task<int>.
  • A visszatérési típusnak a következőnek kell lennievoid: , intSystem.Threading.Tasks.Taskvagy System.Threading.Tasks.Task<int>.
  • Nem lehet részleges módszer (15.6.9. §) végrehajtás nélkül.
  • A paraméterlista vagy üres, vagy egyetlen típusú értékparaméterrel rendelkezik string[].

Megjegyzés: A módosítóval rendelkező metódusoknak async pontosan a fent megadott két visszatérési típus egyikével kell rendelkezniük ahhoz, hogy belépési pontnak minősüljenek. Egy async void metódus, vagy egy async másik várt típust visszaadó metódus, például ValueTaskValueTask<int> vagy nem minősül belépési pontnak. végjegyzet

Ha egy programon belül egynél több belépési pontnak minősülő metódust deklarálnak, egy külső mechanizmussal megadhatja, hogy melyik metódus tekinthető az alkalmazás tényleges belépési pontjának. Ha olyan minősítő metódust talál, amelynek visszatérési int típusa vagy void típusa megtalálható, akkor bármely olyan minősítő metódus, amelynek visszatérési System.Threading.Tasks.TaskSystem.Threading.Tasks.Task<int> típusa vagy nem minősül belépési pont metódusnak. Fordítási időhiba, hogy egy program alkalmazásként, pontosan egy belépési pont nélkül fordítható le. Az osztálytárként lefordított programok tartalmazhatnak olyan metódusokat, amelyek alkalmazásbeléptetési pontnak minősülnének, de az eredményként kapott kódtárnak nincs belépési pontja.

A módszer deklarált akadálymentességét (7.5.2. §) általában a deklarációjában meghatározott hozzáférési módosítók (15.3.6. §) határozzák meg, és hasonlóképpen a deklarált akadálymentességet a deklarációban meghatározott hozzáférési módosítók határozzák meg. Annak érdekében, hogy egy adott típusú módszer hívható legyen, mind a típusnak, mind a tagnak elérhetőnek kell lennie. Az alkalmazás belépési pontja azonban különleges eset. A végrehajtási környezet a deklarált akadálymentességtől és az ahhoz tartozó típusdeklarációk deklarált akadálymentességétől függetlenül elérheti az alkalmazás belépési pontját.

Ha a belépési pont metódusának visszatérési típusa System.Threading.Tasks.Task vagy System.Threading.Tasks.Task<int>, a fordítónak szintetizálnia kell egy szinkron belépésipont-metódust, amely a megfelelő Main metódust hívja meg. A szintetizált metódus paraméterekkel és visszatérési típusokkal rendelkezik a Main metódus alapján:

  • A szintetizált metódus paraméterlistája megegyezik a metódus paraméterlistájával Main
  • Ha a Main metódus visszatérési típusa az System.Threading.Tasks.Task, a szintetizált metódus visszatérési típusa a következő: void
  • Ha a Main metódus visszatérési típusa az System.Threading.Tasks.Task<int>, a szintetizált metódus visszatérési típusa a következő: int

A szintetizált metódus végrehajtása az alábbiak szerint történik:

  • A szintetizált metódus meghívja a Main metódust, és argumentumként adja át a string[] paraméter értékét, ha a Main metódus rendelkezik ilyen paraméterrel.
  • Ha a Main metódus kivételt jelez, a kivételt a szintetizált metódus propagálja.
  • Ellenkező esetben a szintetizált belépési pont megvárja, amíg a visszaadott tevékenység befejeződik, és meghívja GetAwaiter().GetResult() a feladatot a paraméter nélküli példány metódusával vagy a §C.3 által leírt bővítménymetódussal. Ha a feladat meghiúsul, GetResult() kivételt ad ki, és ezt a kivételt a szintetizált metódus propagálja.
  • Ha a MainSystem.Threading.Tasks.Task<int>feladat sikeresen befejeződött, int a visszaadott érték GetResult() a szintetizált metódusból lesz visszaadva.

Az alkalmazás tényleges belépési pontja a programon belül deklarált belépési pont, vagy a szintetizált módszer, ha a fent leírt módon szükséges. Ezért a tényleges belépési pont visszatérési típusa mindig void vagy int.

Egy alkalmazás futtatásakor létrejön egy új alkalmazástartomány . Egy alkalmazás több különböző példánya is létezhet ugyanazon a gépen egyszerre, és mindegyiknek saját alkalmazástartománya van. Az alkalmazástartomány az alkalmazásállapot tárolójaként való működéssel teszi lehetővé az alkalmazások elkülönítését. Az alkalmazástartomány tárolóként és határként működik az alkalmazásban és az általa használt osztálykódtárakban meghatározott típusok számára. Az egyik alkalmazástartományba betöltött típusok eltérnek a másik alkalmazástartományba betöltött típusoktól, és az objektumpéldányok nem oszthatók meg közvetlenül az alkalmazástartományok között. Például minden alkalmazástartomány rendelkezik az ilyen típusú statikus változók saját másolatával, és egy típus statikus konstruktorát alkalmazástartományonként legfeljebb egyszer futtatják. A megvalósítások szabadon biztosítják az alkalmazástartományok létrehozására és megsemmisítésére vonatkozó, implementáció által meghatározott szabályzatokat vagy mechanizmusokat.

Az alkalmazás indítása akkor történik, amikor a végrehajtási környezet meghívja az alkalmazás tényleges belépési pontját. Ha a tényleges belépési pont deklarál egy paramétert, akkor az alkalmazás indításakor a megvalósításnak biztosítania kell, hogy a paraméter kezdeti értéke nem null értékű hivatkozás egy sztringtömbre. Ennek a tömbnek nem null értékű hivatkozásokat kell tartalmaznia azokra a sztringekre, úgynevezett application parameters-ekre, amelyeket a gazdagépkörnyezet implementáció által meghatározott értékeket kap az alkalmazás indítása előtt. A cél az, hogy az alkalmazás indítása előtt meghatározott alkalmazásinformációkat az üzemeltetett környezet más helyről adja meg.

Megjegyzés: A parancssort támogató rendszerekben az alkalmazásparaméterek megfelelnek az általánosan parancssori argumentumoknak. végjegyzet

Ha a tényleges belépési pont visszatérési típusa az int, a végrehajtási környezet által a metódus meghívásából származó visszatérési értéket használja az alkalmazás leállítása során (7.2. §).

A fenti helyzeteken kívül a belépési pont metódusai ugyanúgy viselkednek, mint azok, amelyek nem minden szempontból belépési pontok. Különösen akkor, ha a belépési pontot az alkalmazás élettartama során bármely más időpontban hívják meg, például normál metódushívással, a metódusnak nincs különleges kezelése: ha van paraméter, akkor lehet, hogy a kezdő értéke null, vagy nemnull értéke nullhivatkozásokat tartalmazó tömbre hivatkozik. Hasonlóképpen, a belépési pont visszatérési értéke csak a végrehajtási környezetből való meghívásban van különleges jelentőséggel.

7.2 Alkalmazás megszüntetése

A vezérlés visszatérése a végrehajtási környezetbe alkalmazásvégzítésnek nevezik.

Ha az alkalmazás tényleges belépési pont metódusának visszatérési típusa, int és a végrehajtás kivétel nélkül befejeződik, a int visszaadott érték az alkalmazás leállási állapotkódjaként szolgál. A kód célja, hogy lehetővé tegye a sikeres vagy sikertelen kommunikációt a végrehajtási környezettel. Ha a tényleges belépésipont-metódus visszatérési típusa, void és a végrehajtás kivétel nélkül befejeződik, a lezárás állapotkódja .0

Ha a tényleges belépési pont metódusa kivétel miatt leáll (22.4.§), a kilépési kód implementálási definiálva van. Emellett az implementáció alternatív API-kat is biztosíthat a kilépési kód megadásához.

Azt, hogy a véglegesítők (15.13.§) az alkalmazásleállítás részeként futnak-e, implementálás határozza meg.

Megjegyzés: A .NET-keretrendszer megvalósítása minden ésszerű erőfeszítést megtesz annak érdekében, hogy a véglegesítőket (15.13.§) meghívja az összes olyan objektumhoz, amelyet még nem gyűjtöttek össze, kivéve, ha az ilyen törlést elfojtották (például a kódtár metódusának GC.SuppressFinalizehívásával). végjegyzet

7.3 Nyilatkozatok

A C#-programokban lévő deklarációk határozzák meg a program összetevőit. A C#-programok névterek használatával vannak rendszerezve. Ezeket névtér-deklarációkkal vezetik be (14. §), amelyek típusdeklarációkat és beágyazott névtér-deklarációkat tartalmazhatnak. A típusdeklarációk (14.7.§) osztályokat (15. §), szerkezeteket (16. §), interfészeket (19. §), számokat (20. §) és meghatalmazottakat (21. §) határoznak meg. A típusdeklarációban engedélyezett tagtípusok a típusdeklaráció formájától függenek. Például: az osztálydeklarációk tartalmazhatnak állandókra vonatkozó deklarációkat (15.4.§), mezőket (15.5.§), metódusokat (15.6. §), tulajdonságokat (15.7.§), eseményeket (15.8.§), indexelőket (15.9. §) operátorok (15.10.§), példánykonstruktorok (15.11.§), statikus konstruktorok (15.12. §), véglegesítők (15.13. §) és beágyazott típusok (15.3.9. §).

A deklaráció egy nevet határoz meg abban a deklarációs térben , amelyhez a deklaráció tartozik. Fordítási idő hibát jelent, ha két vagy több deklarációval rendelkezik, amelyek azonos nevű tagokat vezetnek be egy deklarációs térben, kivéve a következő eseteket:

  • Ugyanabban a deklarációs térben két vagy több azonos nevű névtér-deklaráció engedélyezett. Az ilyen névtér-deklarációk összesítve egyetlen logikai névteret alkotnak, és egyetlen deklarációs területet osztanak meg.
  • A deklarációk külön programokban, de ugyanabban a névtér deklarációs térben is megoszthatók ugyanazzal a névvel.

    Megjegyzés: Ezek a deklarációk azonban kétértelműséget eredményezhetnek, ha ugyanabban az alkalmazásban szerepelnek. végjegyzet

  • Ugyanabban a deklarációs térben két vagy több azonos nevű, de különálló aláírást tartalmazó metódus engedélyezett (7.6. §).
  • Ugyanabban a deklarációs térben két vagy több típusdeklaráció engedélyezett azonos névvel, de különböző számú típusparaméterrel (7.8.2. §).
  • Két vagy több típusdeklaráció, amelynek a részleges módosítója ugyanabban a deklarációs térben található, ugyanazzal a névvel, azonos számú típusparaméterrel és azonos besorolással (osztály, szerkezet vagy interfész) rendelkezhet. Ebben az esetben a típusdeklarációk egyetlen típushoz járulnak hozzá, és összesítve egyetlen deklarációs területet alkotnak (15.2.7. §).
  • Egy névtér-deklaráció és egy típusdeklaráció ugyanabban a deklarációs térben ugyanazzal a névvel rendelkezhet, ha a típusdeklaráció legalább egy típusparaméterrel rendelkezik (7.8.2. §).

A deklarációs szóközöknek számos különböző típusa van, az alábbiakban leírtak szerint.

  • A program összes összeállítási egységén belül namespace_member_declaration namespace_declaration nélkülegyetlen kombinált deklarációs tér, az úgynevezett globális deklarációs tér tagjai.
  • A program összes fordítási egységén belül namespace_member_declarationnamespace_declarations-ben, amelyek azonos teljes névtérnévvel rendelkeznek, egyetlen egyesített deklarációs tér tagjai.
  • Minden compilation_unit és namespace_body rendelkezik egy alias deklarációs területtel. A compilation_unit vagy namespace_body minden extern_alias_directive és using_alias_directivehozzájárul egy taghoz az alias deklarációs térhez (14.5.2. §).
  • Minden nem részleges osztály-, szerkezet- vagy felületdeklaráció új deklarációs területet hoz létre. Minden részleges osztály-, szerkezet- vagy felületdeklaráció hozzájárul ahhoz a deklarációs térhez, amelyet ugyanazon program minden egyező része megosztott (16.2.4. §). A nevek class_member_declaration s, struct_member_declarations, interface_member_declarations vagy type_parameters segítségével kerülnek be ebbe a deklarációs területre. A túlterhelt példánykonstruktor-deklarációk és a statikus konstruktor-deklarációk kivételével az osztály, a szerkezet vagy a felület nem tartalmazhat olyan tagdeklarációt, amelynek neve megegyezik az osztály, a szerkezet vagy a felület nevével. Egy osztály, szerkezet vagy interfész lehetővé teszi a túlterhelt metódusok és indexelők deklarálását. Továbbá egy osztály vagy szerkezet lehetővé teszi a túlterhelt példányok konstruktorainak és operátorainak deklarálását. Egy osztály, szerkezet vagy felület például több azonos nevű metódusdeklarációt is tartalmazhat, feltéve, hogy ezek a metódusdeklarációk aláírásukban eltérnek (7.6. §). Vegye figyelembe, hogy az alaposztályok nem járulnak hozzá egy osztály deklarációs területéhez, és az alapillesztők nem járulnak hozzá a felület deklarációs területéhez. Így egy származtatott osztály vagy felület egy öröklött tag nevével megegyező nevű tagot deklarálhat. Egy ilyen tag azt mondják, hogy elrejtse az örökölt tagot.
  • Minden deklaráció új deklarációs területet hoz létre. A nevek paramétereken (fixed_parameter és parameter_arrays) és type_parameters-n keresztül kerülnek be ebbe a deklarációs területre.
  • Minden enumerálási deklaráció új deklarációs területet hoz létre. A nevek enum_member_declarations keresztül kerülnek be ebbe a deklarációs területre.
  • Minden metódusdeklaráció, tulajdonságdeklaráció, tulajdonságkiegészítő deklaráció, indexelő deklaráció, indexelő tartozék deklarációja, operátor-deklaráció, példánykonstruktor-deklaráció, névtelen függvény és helyi függvény létrehoz egy új deklarációs területet, egy helyi változó deklarációs teret. A nevek paramétereken (fixed_parameter és parameter_arrays) és type_parameters-n keresztül kerülnek be ebbe a deklarációs területre. Egy tulajdonság vagy egy indexelő beállított tartozéka paraméterként adja meg a nevet value . A függvény tagjának, névtelen függvényének vagy helyi függvényének törzse , ha van ilyen, beágyazottnak minősül a helyi változó deklarációs területén belül. Ha egy helyi változó deklarációs területe és egy beágyazott helyi változódeklarációs tér azonos nevű elemeket tartalmaz, a beágyazott helyi név hatókörén belül a külső helyi név el van rejtve (7.7.1. §) a beágyazott helyi névvel.
  • A tagdeklarációkban, névtelen függvényekben és helyi függvényekben további helyi változódeklarációs szóközök is előfordulhatnak. Ezekbe a deklarációs szóközökbe az s, declaration_expressions, declaration_statements és exception_specifiers mintákonkeresztül kerülnek be nevek. Előfordulhat, hogy a helyi változódeklarációs szóközök beágyazottak, de hiba, ha egy helyi változódeklarációs tér és egy beágyazott helyi változódeklarációs tér azonos nevű elemeket tartalmaz. Így a beágyazott deklarációs térben nem deklarálható egy helyi változó, helyi függvény vagy állandó ugyanazzal a névvel, mint egy paraméter, típusparaméter, helyi változó, helyi függvény vagy állandó egy belefoglalt deklarációs térben. Két deklarációs szóköz is tartalmazhat azonos nevű elemeket, ha egyik deklarációs terület sem tartalmazza a másikat. A helyi deklarációs szóközöket a következő szerkezetek hozzák létre:
    • Egy mező- és tulajdonságdeklaráció minden variable_initializer saját helyi változódeklarációs területet vezet be, amely nincs beágyazva más helyi változódeklarációs térbe.
    • Egy függvénytag, névtelen függvény vagy helyi függvény törzse, ha van ilyen, létrehoz egy helyi változódeklarációs helyet, amely a függvény helyi változódeklarációs területén belül beágyazottnak minősül.
    • Minden constructor_initializer létrehoz egy helyi változó deklarációs területet a példánykonstruktor-deklarációba ágyazva. A konstruktor törzsének helyi változódeklarációs területe ebben a helyi változódeklarációs térben van beágyazva.
    • Minden blokk, switch_block, specific_catch_clause, iteration_statement és using_statement létrehoz egy beágyazott helyi változódeklarációs helyet.
    • Minden olyan embedded_statement , amely nem közvetlenül része egy statement_list beágyazott helyi változódeklarációs helyet hoz létre.
    • Minden switch_section létrehoz egy beágyazott helyi változó deklarációs területet. A közvetlenül a switch_section statement_listdeklarált változók (de nem a statement_list belüli beágyazott helyi változódeklarációs térben) közvetlenül a switch_section helyett közvetlenül a switch_block helyi változódeklarációs területéhez lesznek hozzáadva.
    • Egy query_expression szintaktikai fordítása (12.22.3. §) egy vagy több lambdakifejezést is tartalmazhat. Névtelen függvényekként ezek mindegyike létrehoz egy helyi változó deklarációs területet a fent leírtak szerint.
  • Minden blokk vagy switch_block külön deklarációs területet hoz létre a címkék számára. A nevek labeled_statement s-eken keresztül kerülnek be ebbe a deklarációs területre, és a nevekre goto_statements-eken keresztül hivatkoznak. A blokkok címkedeklarációs területe tartalmazza a beágyazott blokkokat. Így egy beágyazott blokkon belül nem lehet egy címkével azonos nevű címkét deklarálni egy beágyazott blokkban.

Megjegyzés: Meglepő kódhoz vezethet, ha a közvetlenül egy switch_section belül deklarált változók a switch_block helyi változódeklarációs területéhez vannak hozzáadva a switch_section helyett. Az alábbi példában a helyi változó y az alapértelmezett eset kapcsolószakaszában található, annak ellenére, hogy a 0. eset kapcsolószakaszában megjelenik a deklaráció. A helyi változó z nincs hatókörben az alapértelmezett eset kapcsolószakaszában, mivel annak a kapcsolószakasznak a helyi változódeklarációs területén kerül bevezetésre, amelyben a deklaráció történik.

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;
}

végjegyzet

A nevek deklarálásának szöveges sorrendje általában nincs jelentősége. A szöveges sorrend különösen nem jelentős a névterek, állandók, metódusok, tulajdonságok, események, indexelők, operátorok, példánykonstruktorok, véglegesítők, statikus konstruktorok és típusok deklarációjához és használatához. A deklarálási sorrend a következő módokon jelentős:

  • A meződeklarációk deklarációs sorrendje határozza meg az inicializálók végrehajtásának sorrendjét (15.5.6.2. §, 15.5.6.3. §).
  • A helyi változókat használat előtt meg kell határozni (7.7. §).
  • Az enumerálási tagdeklarációk deklarációs sorrendje (20.4. §) akkor jelentős , ha constant_expression értékek nincsenek megadva.

Példa: A névtér deklarációs területe "nyitott végű", és két azonos teljes névvel rendelkező névtér-deklaráció is hozzájárul ugyanahhoz a deklarációs térhez. Példa:

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

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

A fenti két névtérdeklaráció ugyanahhoz a deklarációs térhez járul hozzá, ebben az esetben két osztályt deklarál a teljes névvel Megacorp.Data.Customer és Megacorp.Data.Ordera . Mivel a két deklaráció ugyanahhoz a deklarációs térhez járul hozzá, fordítási idő hibát okozott volna, ha mindegyik tartalmazott egy azonos nevű osztály deklarációját.

záró példa

Megjegyzés: A fentieknek megfelelően a blokkok deklarációs területe tartalmazza a beágyazott blokkokat. A következő példában tehát a FG metódusok fordítási időt eredményeznek, mivel a név i a külső blokkban deklarálva van, és a belső blokkban nem módosítható újra. A metódusok azonban H érvényesek, I mivel a két inem beágyazott blokkban vannak deklarálva.

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();
        }
    }
}

végjegyzet

7.4 Tagok

7.4.1 Általános

A névterek és a típusok tagokkalrendelkeznek.

Megjegyzés: Az entitások tagjai általában egy minősített név használatával érhetők el, amely az entitásra való hivatkozással kezdődik, majd egy "." jogkivonattal, majd a tag nevével. végjegyzet

Egy típus tagjai vagy a típusdeklarációban vannak deklarálva, vagy a típus alaposztályából öröklődnek. Ha egy típus öröklődik egy alaposztálytól, az alaposztály minden tagja a példánykonstruktorok, a véglegesítők és a statikus konstruktorok kivételével a származtatott típus tagjaivá válik. Az alaposztálytag deklarált akadálymentessége nem szabályozza, hogy a tag öröklött-e– az öröklés kiterjed minden olyan tagra, amely nem példánykonstruktor, statikus konstruktor vagy véglegesítő.

Megjegyzés: Előfordulhat azonban, hogy az örökölt tag nem érhető el származtatott típusban, például a deklarált akadálymentesség miatt (7.5.2. §). végjegyzet

7.4.2 Névtértagok

A globális névtér tagjai azok a névterek és típusok, amelyek nem foglalnak magában névteret. Ez közvetlenül a globális deklarációs térben deklarált neveknek felel meg.

A névtérben deklarált névterek és típusok ennek a névtérnek a tagjai. Ez közvetlenül a névtér deklarációs területén deklarált neveknek felel meg.

A névterek nem rendelkeznek hozzáférési korlátozásokkal. Privát, védett vagy belső névterek deklarálása nem lehetséges, és a névtérnevek mindig nyilvánosan elérhetők.

7.4.3 A tagok strukturált

A szerkezet tagjai a strukturálásban deklarált tagok, valamint a struktúra közvetlen alaposztályából és a közvetett alaposztályból System.ValueTypeobjectöröklődő tagok.

Az egyszerű típus tagjai közvetlenül az egyszerű típus által aliasolt szerkezettípus tagjainak felelnek meg (8.3.5. §).

7.4.4 Enumerálási tagok

Az enumerálás tagjai az enumerálásban deklarált állandók, valamint az enumerálás közvetlen alaposztályából System.Enum és a közvetett alaposztályokból System.ValueType öröklődő tagok.object

7.4.5 Osztálytagok

Az osztály tagjai az osztályban deklarált tagok és az alaposztálytól örökölt tagok (kivéve az alaposztályt nem tartalmazó osztályt object ). Az alaposztálytól örökölt tagok közé tartoznak az alaposztály állandói, mezői, metódusai, tulajdonságai, eseményei, indexelői, operátorai és típusai, de az alaposztály példánykonstruktorai, véglegesítői és statikus konstruktorai nem. Az alaposztálytagok a kisegítő lehetőségeiktől függetlenül öröklődnek.

Az osztálydeklarációk tartalmazhatnak állandók, mezők, metódusok, tulajdonságok, események, indexelők, operátorok, példánykonstruktorok, véglegesítők, statikus konstruktorok és típusok deklarációit.

A (object) és (string. §) tagok közvetlenül az általuk aliasként megadott osztálytípusok tagjainak felelnek meg.

7.4.6 Felülettagok

Az interfész tagjai az interfészben és az interfész minden alapfelületén deklarált tagok.

Megjegyzés: Az osztály object tagjai szigorúan nem tagjai semmilyen felületnek (19.4. §). Az osztály object tagjai azonban bármely felülettípusban elérhetőek a tagok keresésével (12.5. §). végjegyzet

7.4.7 Tömbtagok

A tömb tagjai az osztályból System.Arrayöröklődő tagok.

7.4.8 Tagok delegálása

A meghatalmazott a tagokat az osztályból System.Delegateörökli. Emellett tartalmaz egy metódust Invoke is, amelynek neve megegyezik a deklarációban megadott visszatérési típussal és paraméterlistával (21.2. §). Ennek a módszernek a meghívása ugyanúgy viselkedik, mint egy meghatalmazotti meghívás (21.6. §) ugyanazon meghatalmazottpéldányon.

A megvalósítás további tagokat biztosíthat, akár öröklés útján, akár közvetlenül a meghatalmazottban.

7.5 Tagok hozzáférése

7.5.1 Általános

A tagok deklarációi lehetővé teszik a tagok hozzáférésének ellenőrzését. A tag akadálymentességét a tag deklarált akadálymentessége (7.5.2. §) és az azonnal tartalmazó típus akadálymentessége határozza meg, ha van ilyen.

Ha egy adott taghoz való hozzáférés engedélyezve van, a rendszer azt mondja, hogy a tag elérhető. Ezzel szemben, ha egy adott taghoz való hozzáférés nem engedélyezett, a tag elérhetetlennek minősül. A tagok hozzáférése akkor engedélyezett, ha a hozzáférés szöveges helye szerepel a tag akadálymentességi tartományában (7.5.3. §).

7.5.2 Deklarált akadálymentesség

Egy tag deklarált akadálymentessége a következők egyike lehet:

  • Nyilvános, amelyet úgy választunk ki, hogy beleveszünk egy public módosítót a tagdeklarációba. Az intuitív jelentése public "hozzáférés nem korlátozott".
  • Védett, amelyet úgy választunk ki, hogy beleveszünk egy protected módosítót a tagdeklarációba. Az intuitív jelentés " protected a hozzáférést a tartalmazó osztályra vagy interfészre, vagy azokat tartalmazó típusból származtatott osztályokra vagy interfészekre korlátozza".
  • Belső, amelyet úgy választunk ki, hogy beleveszünk egy internal módosítót a tagdeklarációba. Az intuitív jelentése internal "hozzáférés korlátozott ehhez a szerelvényhez".
  • Védett belső, amely úgy van kiválasztva, hogy egy és egy protected módosító is internal szerepel a tag deklarációjában. Az intuitív jelentése protected internal "elérhető ebben a szerelvényben, valamint a típusok származtatják a tartalmazó osztály".
  • Privát védelem, amely úgy van kiválasztva, hogy a tag deklarációjában egy private és egy protected módosító is szerepel. Az intuitív jelentés " private protected elérhető ezen a szerelvényen belül az azt tartalmazó osztály és az azt tartalmazó osztályból származtatott típusok által".
  • Privát, amelyet úgy választunk ki, hogy beleveszünk egy private módosítót a tagdeklarációba. Az intuitív jelentése private "hozzáférés korlátozott a tartalmazó típus".

Attól függően, hogy milyen környezetben történik egy tag deklarációja, csak bizonyos típusú deklarált akadálymentesség engedélyezett. Továbbá, ha egy tag deklarációja nem tartalmaz hozzáférési módosítókat, a deklarálás alapjául szolgáló környezet határozza meg az alapértelmezett deklarált akadálymentességet.

  • A névterek implicit módon akadálymentességet deklaráltak public . A névtér-deklarációkban nem engedélyezett hozzáférés-módosítók használata.
  • A közvetlenül fordítási egységekben vagy névterekben deklarált típusok (szemben más típusokkal) rendelkezhetnek vagy public deklarálhatnak internal akadálymentességet, és alapértelmezés szerint internal a deklarált akadálymentességet.
  • Az osztálytagok a deklarált akadálymentesség bármely engedélyezett típusával rendelkezhetnek, és alapértelmezés szerint a deklarált akadálymentességhez private is tartozhatnak.

    Megjegyzés: Az osztály tagjaként deklarált típus bármilyen engedélyezett típusú deklarált akadálymentességgel rendelkezhet, míg a névtér tagjaként deklarált típusok csak public vagy internal deklarált akadálymentességgel rendelkezhetnek. végjegyzet

  • A strukturált szerkezet tagjai a deklarált akadálymentességhez tartozhatnak, publicvagy internal deklarálhatják privateaz akadálymentességet, és alapértelmezés szerint private a deklarált akadálymentességet, mivel a szerkezetek implicit módon lezárva vannak. A szerkezetben struct bevezetett (azaz nem az adott struktúra által örökölt) tagokat nem lehet protected, protected internalilletve private protected nem deklarált akadálymentesség.

    Megjegyzés: A struktúra tagjaként deklarált típus lehet public, internalvagy private deklarált akadálymentesség, míg egy névtér tagjaként deklarált típus csak public vagy internal deklarált akadálymentességgel rendelkezhet. végjegyzet

  • A felület tagjai implicit módon akadálymentességet deklaráltak public . Az interfésztag-deklarációkban nem engedélyezett hozzáférési módosítók használata.
  • A számbavételi tagok implicit módon akadálymentességet deklaráltak public . A számbavételi tag deklarációiban nem engedélyezett hozzáférés-módosító.

7.5.3 Akadálymentességi tartományok

A tagok akadálymentességi tartománya a program szövegének (esetleg különálló) szakaszaiból áll, amelyekben a taghoz való hozzáférés engedélyezett. Egy tag akadálymentességi tartományának meghatározásához a rendszer azt mondja, hogy a tag legfelső szintű, ha nem egy típuson belül deklarálják, és egy tag beágyazottnak minősül, ha azt egy másik típuson belül deklarálják. Ezenkívül a program programszövege a program összes fordítási egységében található összes szöveg, a típus programszövege pedig az adott típusú type_declarations-ben található összes szövegként van definiálva (beleértve a típusba beágyazott típusokat is).

Az előre definiált típusú (például object, intvagy double) akadálymentességi tartomány korlátlan.

A programban T deklarált legfelső szintű kötetlen típusú (P. §) akadálymentességi tartomány a következőképpen van definiálva:

  • Ha a deklarált akadálymentesség T nyilvános, akkor az akadálymentességi tartomány T a program szövege P és bármely olyan program, amely hivatkozik Prá.
  • Ha a deklarált akadálymentesség T belső, az akadálymentességi tartomány T a program szövege P.

Megjegyzés: Ezekből a definíciókból következik, hogy a legfelső szintű kötetlen típus akadálymentességi tartománya mindig legalább annak a programnak a programszövege, amelyben a típus deklarálva van. végjegyzet

A létrehozott típus T<A₁, ..., Aₑ> akadálymentességi tartománya a kötetlen általános típus T akadálymentességi tartományának és a típusargumentumok akadálymentességi tartományának metszete A₁, ..., Aₑ.

A programon Mbelüli típusban T deklarált beágyazott tag P akadálymentességi tartománya a következőképpen van definiálva (tekintve, hogy M maga is lehet típus):

  • Ha a deklarált akadálymentesség M az public, akkor az akadálymentességi tartomány M az akadálymentességi Ttartomány.
  • Ha a deklarált akadálymentesség M az protected internal, akkor legyen D a program szövegének P és bármilyen származtatott programszövegnek az egyesítése , amely kívül TPvan deklarálva . Az akadálymentességi M tartomány az akadálymentességi tartomány metszete TD.
  • Ha a deklarált akadálymentesség M azprivate protected, akkor legyen D a program szövegének és a program szövegének metszete PT, illetve bármilyen, a programból Tszármaztatott típus. Az akadálymentességi M tartomány az akadálymentességi tartomány metszete TD.
  • Ha a deklarált akadálymentesség az , akkor legyen M a program szövegének protectedés a program szövegének egyesítését bármilyen típusú származtatottD.TT Az akadálymentességi M tartomány az akadálymentességi tartomány metszete TD.
  • Ha a deklarált akadálymentesség M az internal, akkor az akadálymentességi tartomány M az akadálymentességi tartomány és a program szövegének T metszete P.
  • Ha a deklarált akadálymentesség M az private, akkor az akadálymentességi tartomány M a program szövege T.

Megjegyzés: Ezekből a definíciókból következik, hogy a beágyazott tag akadálymentességi tartománya mindig legalább a tag deklarálási típusának programszövege. Ebből az is következik, hogy a tagok akadálymentességi tartománya soha nem befogadóbb, mint a tag deklarált típusú akadálymentességi tartománya. végjegyzet

Megjegyzés: Intuitív módon, amikor egy típushoz vagy taghoz M fér hozzá, a rendszer az alábbi lépéseket értékeli ki, hogy a hozzáférés engedélyezve legyen:

  • Először is, ha M egy típuson belül deklarálva van (a fordítási egység vagy a névtér helyett), fordítási időhiba lép fel, ha ez a típus nem érhető el.
  • Ha igenM, public akkor a hozzáférés engedélyezett.
  • Ellenkező esetben a hozzáférés akkor engedélyezett, Mprotected internalha a programon belül történik, amelyben M deklarálva van, vagy ha az a deklarált osztályból származtatott osztályon M belül történik, és a származtatott osztálytípuson keresztül történik (7.5.4. §).
  • Ellenkező esetben a hozzáférés akkor engedélyezett, Mprotectedha az a deklarált osztályon M belül történik, vagy ha a deklarált osztályból származtatott osztályon M belül történik, és a származtatott osztálytípuson keresztül történik (7.5.4. §).
  • M internalEllenkező esetben a hozzáférés akkor engedélyezett, ha az abban a programban történik, amelyben M deklarálva van.
  • M privateEllenkező esetben a hozzáférés akkor engedélyezett, ha a deklarált típuson M belül történik.
  • Ellenkező esetben a típus vagy tag nem érhető el, és fordítási időhiba lép fel. végjegyzet

Példa: Az alábbi kódban

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;
    }
}

az osztályok és a tagok a következő akadálymentességi tartományokkal rendelkeznek:

  • Az akadálymentességi tartomány AA.X korlátlan.
  • A ( , A.Y, B, B.X, B.YB.C) akadálymentességi tartományaB.C.X, és B.C.Y az azt tartalmazó program programszövege.
  • Az akadálymentességi A.Z tartomány a program szövege A.
  • Az akadálymentességi tartomány B.ZB.D és a program szövegeB, beleértve az és B.Ca program szövegét B.D is.
  • Az akadálymentességi B.C.Z tartomány a program szövege B.C.
  • Az akadálymentességi tartomány B.D.XB.D.Y és a program szövegeB, beleértve az és B.Ca program szövegét B.D is.
  • Az akadálymentességi B.D.Z tartomány a program szövege B.D. Ahogy a példa is mutatja, a tagok akadálymentességi tartománya soha nem nagyobb, mint egy tartalmú típus. Például annak ellenére, hogy minden X tag rendelkezik nyilvánosan deklarált akadálymentességgel, az A.X összes olyan akadálymentességi tartománnyal rendelkezik, amely egy adott típusú korlátozás alá van állítva.

záró példa

A 7.4. §-ban leírtak szerint az alaposztály minden tagját, kivéve például a konstruktorokat, a véglegesítőket és a statikus konstruktorokat, származtatott típusok öröklik. Ebbe beletartoznak még az alaposztály privát tagjai is. A privát tagok akadálymentességi tartománya azonban csak a tag deklarált típusának programszövegét tartalmazza.

Példa: Az alábbi kódban

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

az B osztály örökli a privát tagot x az A osztálytól. Mivel a tag privát, csak a class_bodyAérhető el. Így a hozzáférés b.x sikeres a A.F metódusban, de a metódusban B.F meghiúsul.

záró példa

7.5.4 Védett hozzáférés

Ha egy protected vagy private protected több példányhoz a deklarált osztály programszövegén kívül férnek hozzá, és ha egy protected internal példánytag a deklarált program programszövegén kívül érhető el, a hozzáférés egy osztálydeklaráción belül történik, amely abból az osztályból származik, amelyben deklarálták. Továbbá a hozzáférésnek az adott származtatott osztálytípus vagy az abból létrehozott osztálytípus egy példányán keresztül kell végbejutnia. Ez a korlátozás megakadályozza, hogy egy származtatott osztály hozzáférjen más származtatott osztályok védett tagjaihoz, még akkor is, ha a tagok öröklődnek ugyanabból az alaposztályból. A megadott vagy protected hozzáféréssel private protected definiált példány-illesztőtagok nem érhetők el az class adott felületről vagy struct azok implementálásához; ezek csak származtatott felületekről érhetők el. A típusok azonban definiálhatják az classstruct általa implementált felületen deklarált felülírt protected példánytagokat.

Legyen B egy alaposztály, amely védett példánytagot Mdeklarál, és legyen D egy olyan osztály, amely a következőből Bszármazik: . A class_bodyDa hozzáférés M az alábbi űrlapok egyikét veheti igénybe:

  • Az űrlap nem minősített type_name vagy primary_expression.M
  • Az , feltéve, hogy E.M az osztály típusa E vagy az abból származtatott Tosztály, ahol T az osztályT, vagy egy osztálytípus, amelyből Dkészült.
  • Az .
  • Az űrlap base[ argument_list].

Ezen hozzáférési formákon kívül a származtatott osztályok hozzáférhetnek egy alaposztály védett példánykonstruktorához egy constructor_initializer (15.11.2. §).

Példa: Az alábbi kódban

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

belül Amindkét példányon x keresztül lehet hozzáférniA, mivel Bmindkét esetben a hozzáférés egy példányon vagy egy, a forrásból Aszármaztatott osztályon keresztülA. A rendszeren belül Bazonban nem lehet hozzáférni x egy példányon Akeresztül , mivel A nem származik belőle B.

záró példa

Példa:

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";
    }
}

Itt a három feladat x engedélyezett, mert mindegyik az általános típusból létrehozott osztálytípusok példányain keresztül történik.

záró példa

Megjegyzés: Egy általános osztályban deklarált védett tag akadálymentességi tartománya (7.5.3. §) tartalmazza az összes osztálydeklaráció programszövegét, amely az adott általános osztályból létrehozott bármely típusból származik. A példában:

class C<T>
{
    protected static T x;
}

class D : C<string>
{
    static void Main()
    {
        C<int>.x = 5;
    }
}

a tagra protectedC<int>.xD való hivatkozás akkor is érvényes, ha az osztály D a következőből C<string>származik: . végjegyzet

7.5.5 Akadálymentességi korlátozások

A C#-nyelv számos szerkezetének legalább olyan akadálymentesnek kell lennie, mint egy tag vagy egy másik típus. Egy típusnak T legalább olyan akadálymentesnek kell lennie, mint egy tag vagy típus M , ha az akadálymentességi tartomány T az akadálymentességi tartomány szuperhalmaza M. Más szóval legalább olyan akadálymentes, T mintha MT minden olyan környezetben elérhető volna, amelyben M elérhető.

A következő akadálymentességi korlátozások léteznek:

  • Az osztálytípus közvetlen alaposztályának legalább olyan hozzáférhetőnek kell lennie, mint maga az osztálytípus.
  • Az interfésztípus explicit alapillesztőinek legalább olyan hozzáférhetőnek kell lenniük, mint maga az interfésztípus.
  • A delegált típusú visszatérési típusnak és paramétertípusoknak legalább olyan hozzáférhetőnek kell lenniük, mint maga a delegált típus.
  • Az állandó típusának legalább olyan akadálymentesnek kell lennie, mint maga az állandó.
  • A mező típusának legalább olyan hozzáférhetőnek kell lennie, mint maga a mező.
  • A metódus visszatérési típusának és paramétertípusának legalább olyan hozzáférhetőnek kell lennie, mint maga a módszer.
  • Az ingatlan típusának legalább olyan akadálymentesnek kell lennie, mint maga az ingatlan.
  • Az esemény típusának legalább olyan akadálymentesnek kell lennie, mint maga az esemény.
  • Az indexelő típusának és paramétertípusainak legalább olyan hozzáférhetőnek kell lenniük, mint maga az indexelő.
  • Az operátor visszatérési típusának és paramétertípusának legalább olyan hozzáférhetőnek kell lennie, mint maga az operátor.
  • A példánykonstruktor paramétertípusainak legalább olyan hozzáférhetőnek kell lenniük, mint maga a példánykonstruktor.
  • Egy típusparaméter interfész- vagy osztálytípus-korlátozásának legalább olyan akadálymentesnek kell lennie, mint a kényszert deklaráló tagnak.

Példa: Az alábbi kódban

class A {...}
public class B: A {...}

az B osztály fordítási időt eredményez, mert A legalább nem olyan elérhető, mint a B.

záró példa

Példa: Hasonlóképpen, a következő kódban

class A {...}

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

a H metódus B fordítási időt eredményez, mert a visszatérési típus A nem legalább olyan elérhető, mint a metódus.

záró példa

7.6 Aláírások és túlterhelés

A metódusokat, a példánykonstruktorokat, az indexelőket és az operátorokat a következőkjellemzik:

  • A metódus aláírása a metódus nevéből, a típusparaméterek számából, valamint az egyes paraméterek típusából és paraméterátadási módjából áll, amelyeket a balról jobbra haladó sorrendben kell figyelembe venni. E célból a metódus bármely olyan típusparaméterét, amely egy paraméter típusában fordul elő, nem a neve, hanem a metódus típusparaméter-listájában elfoglalt sorszáma azonosítja. A metódus aláírása nem tartalmazza a visszatérési típust, a paraméterneveket, a típusparaméter-neveket, a típusparaméter-megkötéseket, a vagy params paramétermódosítókatthis, és azt sem, hogy a paraméterek kötelezőek vagy nem kötelezőek-e.
  • A példánykonstruktor aláírása az egyes paraméterek típusából és paraméterátadási módjából áll, a balról jobbra haladó sorrendben figyelembe véve. A példánykonstruktor aláírása nem tartalmazza a params jobb szélső paraméterhez megadható módosítót, és azt sem, hogy a paraméterek kötelezőek vagy nem kötelezőek-e.
  • Az indexelő aláírása az egyes paraméterek típusából áll, balról jobbra haladva. Az indexelő aláírása nem tartalmazza az elemtípust, és nem tartalmazza a params megfelelő paraméterhez megadható módosítót sem, sem azt, hogy a paraméterek kötelezőek vagy nem kötelezőek-e.
  • Az operátor aláírása az operátor nevéből és az egyes paraméterek típusából áll, a balról jobbra haladva. Az operátor aláírása nem tartalmazza az eredménytípust.
  • A konverziós operátor aláírása a forrástípusból és a céltípusból áll. A konverziós operátor implicit vagy explicit besorolása nem része az aláírásnak.
  • Két azonos tagú aláírás (metódus, példánykonstruktor, indexelő vagy operátor) azonos aláírásnak minősül, ha ugyanazzal a névvel, típusparaméterek számával, paraméterek számával és paraméterátadási módokkal rendelkezik, és az identitásátalakítás a megfelelő paraméterek típusai között létezik (10.2.2. §).

Az aláírások lehetővé teszik a tagok túlterhelését az osztályokban, a szerkezetekben és a felületeken:

  • A metódusok túlterhelése lehetővé teszi, hogy egy osztály, struktúra vagy interfész több azonos nevű metódust deklaráljon, feltéve, hogy az aláírásuk egyedi az adott osztályon, szerkezeten vagy felületen belül.
  • A példánykonstruktorok túlterhelése lehetővé teszi, hogy egy osztály vagy szerkezet több példánykonstruktort deklaráljon, feltéve, hogy az aláírásuk egyedi az adott osztályon vagy szerkezeten belül.
  • Az indexelők túlterhelése lehetővé teszi, hogy egy osztály, szerkezet vagy felület több indexelőt deklaráljon, feltéve, hogy az aláírásuk egyedi az adott osztályon, szerkezeten vagy interfészen belül.
  • Az operátorok túlterhelése lehetővé teszi, hogy egy osztály vagy struktúra több azonos nevű operátort deklaráljon, feltéve, hogy az aláírásuk egyedi az adott osztályon vagy szerkezeten belül.

Bár ina , outés ref a paramétermódosítók egy aláírás részét képezik, az egyetlen típusban deklarált tagok nem térhetnek el az aláírásban kizárólag inaz , outés ref. Fordítási időhiba akkor fordul elő, ha két tag azonos típusú aláírással van deklarálva, amely azonos lenne, ha mindkét metódus outin összes paramétere módosítóra módosulna ref . Egyéb aláírás-egyeztetés céljából (például elrejtés vagy felülbírálás) inoutref az aláírás részét képezik, és nem egyeznek egymással.

Megjegyzés: Ez a korlátozás lehetővé teszi, hogy a C#-programok könnyen lefordíthatók legyenek olyan platformon való futtatásra, amely nem biztosít módot a kizárólag ina , outés ref. végjegyzet

Az aláírások összehasonlításakor a típusok object nem dynamic különböztethetők meg. Ezért azok a tagok, akik egyetlen típusban deklarálva vannak, amelyek aláírásai csak a lecseréléssel objectdynamic különböznek, nem engedélyezettek.

Példa: Az alábbi példa a túlterhelt metódus deklarációit és aláírásait mutatja be.

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
}

Vegye figyelembe, hogy bármely in, outés ref paramétermódosító (§15.6.2) az aláírás része. Így, F(int), F(in int), F(out int) és F(ref int) mind egyedi aláírások. Azonban , F(in int)F(out int) és F(ref int) nem deklarálható ugyanazon a felületen belül, mert aláírásaik kizárólag a in, outés ref. Azt is vegye figyelembe, hogy a visszatérési típus és a params módosító nem része az aláírásnak, ezért nem lehet túlterhelni kizárólag a visszatérési típus, illetve a params módosító felvétele vagy kizárása alapján. Ezért a módszerek F(int) deklarációi és F(params string[]) a fent azonosítottak fordítási időt eredményeznek. záró példa

7.7 Hatókörök

7.7.1 Általános

A név hatóköre a programszöveg azon régiója, amelyen belül a névvel deklarált entitásra hivatkozhat a név minősítése nélkül. A hatókörök beágyazhatók, és a belső hatókörök újraértelmezhetik a név jelentését egy külső hatókörből. (Ez azonban nem szünteti meg a 7.3. §-ban előírt korlátozást, amely szerint beágyazott blokkon belül nem lehet helyi változót vagy helyi állandót deklarálni egy olyan névvel, mint egy helyi változó vagy helyi állandó egy beágyazott blokkban.) A külső hatókörből származó név ezután a belső hatókör által lefedett programszöveg régiójában lesz elrejtve, és a külső névhez való hozzáférés csak a név minősítésével lehetséges.

  • A namespace_member_declaration által deklarált névtértag hatóköre (14.6. §) nem foglal magában namespace_declaration a teljes programszöveget.

  • Egy olyan névtértag hatóköre, amelyet egy namespace_member_declaration deklarált egy

  • A extern_alias_directive által meghatározott név hatóköre (14.4. §) a compilation_unit vagy namespace_body közvetlenül tartalmazóusing_directive, global_attributes és namespace_member_declaration s-ére terjed ki. A extern_alias_directive nem járul hozzá új tagokhoz a mögöttes deklarációs térhez. Más szóval egy extern_alias_directive nem tranzitív, hanem csak a compilation_unit vagy namespace_body érinti, amelyben előfordul.

  • A using_directive által definiált vagy importált név hatóköre (14.5. §) kiterjed azon compilation_unit vagy namespace_body global_attributes és namespace_member_declaration, amelyben a using_directive történik. Egy using_directive egy adott compilation_unit vagy namespace_body nulla vagy több névteret vagy típusnevet tehet elérhetővé, de nem járul hozzá új tagokhoz a mögöttes deklarációs térhez. Más szóval a using_directive nem tranzitív, hanem csak a compilation_unit vagy namespace_body érintik.

  • Egy type_parameter_list által egy class_declaration (§15.2) keretében deklarált típusparaméter hatóköre az adott class_base, type_parameter_constraints_clause és class_body elemekből áll.

    Megjegyzés: Az osztálytagokkal ellentétben ez a hatókör nem terjed ki a származtatott osztályokra. végjegyzet

  • Egy struct_declaration type_parameter_listáltal deklarált típusparaméter hatóköre (16.2. §) az adott struct_declaration struct_interfaces, type_parameter_constraints_clauseés struct_body.

  • Egy interface_declaration type_parameter_list által deklarált típusparaméter hatóköre (19.2. §) az adott interface_declaration interface_base, type_parameter_constraints_clauseés interface_body.

  • Egy delegate_declaration type_parameter_list által deklarált típusparaméter hatóköre (21.2. §) az adott delegate_declaration return_type, parameter_list és type_parameter_constraints_clause.

  • Egy method_declaration type_parameter_list által deklarált típusparaméter hatóköre (15.6.1. §) a method_declaration.

  • A class_member_declaration által deklarált tag hatóköre (15.3.1. §) az a class_body, amelyben a deklaráció történik. Ezenkívül az osztálytagok hatóköre a tag akadálymentességi tartományában (7.5.3. §) szereplő származtatott osztályok class_body is kiterjed.

  • A struct_member_declaration által deklarált tag hatóköre (16.3. §) az a struct_body, amelyben a nyilatkozat történik.

  • A enum_member_declaration által deklarált tag hatóköre (20.4. §) az a enum_body , amelyben a nyilatkozat történik.

  • Egy method_declaration deklarált paraméter hatóköre (15.6. §) az adott method_declaration method_body vagy ref_method_body.

  • Egy indexer_declaration deklarált paraméter hatóköre (15.9. §) a indexer_declaration indexer_body.

  • Egy operator_declaration deklarált paraméter hatóköre (15.10. §) a operator_declaration operator_body.

  • A constructor_declaration deklarált paraméter hatóköre (15.11. §) a constructor_declaration constructor_initializer és blokkja.

  • A lambda_expression deklarált paraméter hatóköre (12.21. §) a lambda_expression lambda_expression_body.

  • Egy anonymous_method_expression deklarált paraméter hatóköre (12.21. §) a anonymous_method_expressionblokkja.

  • A labeled_statement deklarált címke hatóköre (13.5. §) az a blokk, amelyben a deklaráció történik.

  • Egy local_variable_declaration deklarált helyi változó hatóköre (13.6.2. §) az a blokk, amelyben a deklaráció történik.

  • Egy utasítás switch_block deklarált helyi változó hatóköre switch (13.8.3. §) a switch_block.

  • Egy utasítás for_initializer deklarált for

  • A local_constant_declaration deklarált helyi állandó (13.6.3. §) hatóköre az a blokk, amelyben a deklaráció történik. Fordítási időhiba, ha egy helyi állandóra hivatkozik a constant_declarator megelőző szöveges pozícióban.

  • Egy foreach_statement, using_statement, lock_statement vagy query_expression részeként deklarált változó hatókörét az adott szerkezet kiterjesztése határozza meg.

A névtér, osztály, szerkezet vagy számbavételi tag hatókörén belül a tagra a tag deklarációját megelőző szöveges pozícióban lehet hivatkozni.

Példa:

class A
{
    void F()
    {
        i = 1;
    }

    int i = 0;
}

Itt érvényes F , hogy a deklarálása előtt hivatkozzon i rá.

záró példa

A helyi változó hatókörén belül fordítási idejű hiba a helyi változóra hivatkozni a deklarátort megelőző szöveges pozícióban.

Példa:

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

F A fenti módszerben az első hozzárendelés i nem hivatkozik a külső hatókörben deklarált mezőre. Ehelyett a helyi változóra hivatkozik, és fordítási időt eredményez, mert szövegesen megelőzi a változó deklarációját. G A metódusban a deklarátor j inicializálóban való használata j azért érvényes, mert a használat nem előzi meg a deklarátort. A metódusban a H későbbi deklarátor helyesen hivatkozik egy helyi változóra, amely ugyanazon local_variable_declaration belül egy korábbi deklarátorban deklarált.

záró példa

Megjegyzés: A helyi változókra és helyi állandókra vonatkozó hatókörkezelési szabályok úgy vannak kialakítva, hogy garantálják, hogy a kifejezéskörnyezetben használt név jelentése mindig megegyezik egy blokkon belül. Ha egy helyi változó hatóköre csak a deklarációtól a blokk végéig terjedne ki, akkor a fenti példában az első hozzárendelés a példányváltozóhoz, a második hozzárendelés pedig a helyi változóhoz rendelné, ami fordítási idejű hibákhoz vezetne, ha a blokk utasításait később átrendeznék.)

A blokkon belüli név jelentése a név felhasználási környezetétől függően eltérhet. A példában

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"
    }
}

a név A egy kifejezéskörnyezetben a helyi változóra A hivatkozik, és egy típuskörnyezetben az osztályra Ahivatkozik.

végjegyzet

7.7.2 Névtitkolás

7.7.2.1 Általános

Az entitás hatóköre általában több programszöveget foglal magában, mint az entitás deklarációs területe. Az entitás hatóköre tartalmazhat olyan deklarációkat, amelyek új deklarációs szóközöket vezetnek be, amelyek azonos nevű entitásokat tartalmaznak. Az ilyen deklarációk az eredeti entitás elrejtéséhez vezetnek. Ezzel szemben a rendszer azt mondja, hogy az entitás látható, ha nem rejtett.

A név elrejtése akkor fordul elő, ha a hatókörök átfedésben vannak a beágyazással, és ha a hatókörök átfedésben vannak az örökléssel. A elrejtés két típusának jellemzőit az alábbi alklámok ismertetik.

7.7.2.2 Elrejtés beágyazással

A beágyazott névterekbe ágyazott névterek vagy névtereken belüli típusok miatt előfordulhat, hogy az osztályokban vagy a szerkezetekben lévő típusok beágyazása egy helyi függvény vagy lambda eredményeként történik, és a paraméter, a helyi változó és a helyi állandó deklarációk eredményeként.

Példa: Az alábbi kódban

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;
    }
}

a metóduson belül a F példányváltozót i elrejti a helyi változói, G de a i metóduson belül továbbra is a példányváltozóra hivatkozik. A helyi függvényen M1 belül a float i közvetlen külső i. A lambda paraméter i elrejti a float i lambda test belsejét.

záró példa

Ha egy belső hatókörben lévő név elrejt egy nevet egy külső hatókörben, elrejti a név összes túlterhelt előfordulását.

Példa: Az alábbi kódban

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

a hívás F(1) meghívja a F deklaráltat Inner , mert az összes külső előfordulást F elrejti a belső deklaráció. Ugyanebből az okból a hívás F("Hello") fordítási időt eredményez.

záró példa

7.7.2.3 Öröklés útján történő elrejtés

Az öröklés során elrejtett név akkor fordul elő, ha az alaposztályoktól örökölt osztályok vagy szerkezetek újraklarálják a neveket. Az ilyen típusú névtitkolás az alábbi űrlapok egyikét használja:

  • Az osztályban, szerkezetben vagy felületen bevezetett állandó, mező, tulajdonság, esemény vagy típus elrejti az azonos nevű alaposztálytagokat.
  • Az osztályban, a szerkezetben vagy a felületen bevezetett metódus elrejti az azonos nevű nem metódusalapú alaposztály-tagokat, valamint az azonos aláírású alaposztály-metódusokat (7.6. §).
  • Egy osztályban, szerkezetben vagy felületen bevezetett indexelő elrejti az összes azonos aláírású alaptípus-indexelőt (7.6. §).

Az operátor-deklarációkra vonatkozó szabályok (15.10.§) lehetetlenné teszik, hogy egy származtatott osztály egy olyan operátort deklaráljon, amely ugyanazzal az aláírással rendelkezik, mint egy alaposztály operátora. Így az operátorok soha nem rejtik el egymást.

A név külső hatókörből való elrejtésével ellentétben a látható név örökölt hatókörből való elrejtésével figyelmeztetés jelenik meg.

Példa: Az alábbi kódban

class Base
{
    public void F() {}
}

class Derived : Base
{
    public void F() {} // Warning, hiding an inherited name
}

a deklaráció FDerived figyelmeztetést okoz. Az öröklődő név elrejtése kifejezetten nem hiba, mivel ez kizárná az alaposztályok külön fejlődését. Előfordulhat például, hogy a fenti helyzet azért merült fel, mert egy későbbi verzió Base olyan metódust vezetett F be, amely nem volt jelen az osztály egy korábbi verziójában.

záró példa

Az öröklődő név elrejtésével okozott figyelmeztetés a módosító használatával new kiküszöbölhető:

Példa:

class Base
{
    public void F() {}
}

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

A new módosító azt jelzi, hogy a F be " Derived új", és valóban az örökölt tag elrejtésére szolgál.

záró példa

Az új tag deklarációja csak az új tag hatókörén belül rejt el öröklött tagokat.

Példa:

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

A fenti példában F az in deklaráció elrejti Derived az F örököltetBase, de mivel az újnak FDerived privát hozzáférése van, hatóköre nem terjed ki a következőreMoreDerived: . Így a betárcsázás F()MoreDerived.G érvényes, és meghívja Base.F.

záró példa

7.8 Névtér és típusnevek

7.8.1 Általános

A C#-programok több környezetében namespace_name vagy type_name kell megadni.

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?)*
    ;

A namespace_name egy névtérre hivatkozó namespace_or_type_name .

Az alábbiakban ismertetett feloldás után egy namespace_name namespace_or_type_namenévtérre kell hivatkoznia, vagy ellenkező esetben fordítási időhiba lép fel. A namespace_name nem lehetnek típusargumentumok (8.4.2. §).

A type_name egy típusra hivatkozó namespace_or_type_name .

Az alábbiakban ismertetett megoldás után egy type_name namespace_or_type_nameegy típusra kell hivatkoznia, vagy ellenkező esetben fordítási időhiba lép fel.

A namespace_or_type_name egy típusra vagy névtérre hivatkoznak. Egy adott névtérre vagy típusra való feloldás két lépésből áll, amelyek a nyelvtan első részére való felosztásán alapulnak, és ezek egyike a nyelvtani elemek egyike:

  • identifier type_argument_list?
  • qualified_alias_member

és egy záró rész, amely a nyelvtani részlet:

  • ('.' identifier type_argument_list?)*

Először az első rész feloldása történik meg, hogy meghatározza R₀, a kezdő névteret vagy a típust.

Ha a namespace_or_type_name első része egy minősített_álcázási_tag, akkor R₀ az a névtér vagy típus, amelyet az §14.8.1 szakaszban leírtak szerint az azonosítással határozunk meg.

Ellenkező esetben a kezdő rész, amely a nyelvtani azonosító típusa_argument_lista? töredék, az alábbi formák egyikével rendelkezik:

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

ahol:

  • I egyetlen azonosító; és
  • <A₁, ..., Aₓ> egy típus_argumentum_lista, ha nincs megadva típus_argumentum_lista, akkor tekintsük x nullának.

R₀ meghatározása az alábbiak szerint történik:

  • Ha x nulla, és a namespace_or_type_name egy általános metódusdeklarációban (15.6. §) belül, de a metódusfejlécattribútumán kívül jelenik meg, és ha a deklaráció tartalmaz egy típusparamétert (15.2.3. §) a névvelI, akkor R₀ arra a típusparaméterre hivatkozik.
  • Ellenkező esetben, ha a namespace_or_type_name egy típusdeklarációban jelenik meg, akkor minden egyes példánytípusnál T (15.3.2. §) kezdve az adott típusú deklaráció példánytípusától kezdve és az egyes mellékosztályok, szerkezetek vagy felületi deklarációk példánytípusával folytatva (ha vannak ilyenek):
    • Ha x nulla, és a deklaráció T egy névvel Irendelkező típusparamétert tartalmaz, akkor R₀ az adott típusparaméterre hivatkozik.
    • Ellenkező esetben, ha a namespace_or_type_name a típusdeklaráció törzsében jelenik meg, vagy T bármely alaptípusa tartalmaz egy beágyazott akadálymentes típust, amelynek neve I és x típusparaméterei vannak, akkor R₀ az adott típusargumentumokkal létrehozott típusra hivatkozik. Ha több ilyen típus is létezik, a származtatott típuson belül deklarált típus lesz kiválasztva. Ez egy fordítóhiba, ha nincs olyan típus, amely származtatottabb lenne, mint az összes többi.

      Megjegyzés: A nem típusú tagok (állandók, mezők, metódusok, tulajdonságok, indexelők, operátorok, példánykonstruktorok, véglegesítők és statikus konstruktorok) és a különböző számú típusparaméterrel rendelkező típustagok figyelmen kívül lesznek hagyva a namespace_or_type_name jelentésének meghatározásakor. végjegyzet

    • Ellenkező esetben minden névtér Nesetében – kezdve azzal a névtérrel, amelyben a namespace_or_type_name történik – a rendszer a globális névtérrel végződő minden egyes névtérrel folytatva a következő lépéseket értékeli ki, amíg egy entitás nem található:
      • Ha x nulla, és I a névtér neve a következőben Nvan:
        • Ha a namespace_or_type_name helyét egy névtér deklarációja tartalmazza, és a névtér-deklaráció tartalmaz egy N vagy using_alias_directive, amely a nevet egy névtérhez vagy típushoz társítja, akkor a I nem egyértelmű, és fordítási időhiba lép fel.
        • Ellenkező esetben a R₀ utal a I-ben található N nevű névtérre.
      • Ellenkező esetben, ha N egy névvel I és x típusparaméterekkel rendelkező akadálymentes típust tartalmaz, akkor:
        • Ha x nulla, és a namespace_or_type_name helyét egy névtér deklarációja tartalmazza, és a névtér-deklaráció N tartalmaz egy extern_alias_directive vagy using_alias_directive , amely a nevet I egy névtérhez vagy típushoz társítja, akkor a namespace_or_type_name nem egyértelmű, és fordítási időhiba lép fel.
        • R₀ Ellenkező esetben a megadott típusargumentumokkal létrehozott típusra hivatkozik.
      • Ellenkező esetben, ha a namespace_or_type_name helye a következő névtérdeklarációval Nvan elzárva:
        • Ha x nulla, és a névtér-deklaráció tartalmaz egy extern_alias_directive vagy using_alias_directive , amely a nevet I egy importált névtérhez vagy típushoz társítja, akkor R₀ az adott névtérre vagy típusra hivatkozik.
        • Ellenkező esetben, ha a névtér-deklaráció using_namespace_directives által importált névterek pontosan egy név- I és x típusparamétert tartalmazó típust tartalmaznak, akkor R₀ az adott típusargumentumokkal létrehozott típusra hivatkozik.
        • Ellenkező esetben, ha a névtér-deklaráció using_namespace_directives által importált névterek több név- I és x típusparamétert tartalmazó típust tartalmaznak, akkor a namespace_or_type_name nem egyértelmű, és fordítási időhiba lép fel.
    • Ellenkező esetben a namespace_or_type_name nincs meghatározva, és fordítási időhiba lép fel.

Ha a R₀ sikeresen feloldódott, a namespace_or_type_name hátralévő része feloldódik. A záró nyelvtani töredék ismétlődésekből áll k ≥ 0 , és minden ismétlődés tovább oldja a hivatkozott névteret vagy -típust.

Ha k nulla, azaz nincs záradék, akkor a namespace_or_type_nameR₀-ra/ra feloldódik.

Ellenkező esetben minden ismétlődés az alábbi alakok egyikével rendelkezik:

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

ahol I, A és x a fenti módon vannak definiálva.

Minden ismétlésnél n, ahol 1 ≤ n ≤ k, annak feloldása; Rₙamely magában foglalja Rₚ, ahol p = n - 1az előző ismétlés feloldását; a következőképpen határozzuk meg:

  • Ha x nulla, és Rₚ egy névtérre hivatkozik, és Rₚ egy névvel Iellátott beágyazott névteret tartalmaz, akkor Rₙ a beágyazott névtérre hivatkozik.
  • Ellenkező esetben, ha Rₚ egy névtérre hivatkozik, és Rₚ egy név- I és x típusparaméterekkel rendelkező akadálymentes típust tartalmaz, akkor Rₙ az adott típusargumentumokkal létrehozott típusra hivatkozik.
  • Ellenkező esetben, ha Rₚ egy (esetleg létrehozott) osztályra, szerkezetre vagy felülettípusra hivatkozik, és Rₚ vagy bármely alaptípusa tartalmaz egy beágyazott akadálymentes típust, amelynek neve I és x típusparaméterei vannak, akkor Rₙ az adott típusargumentumokkal létrehozott típusra hivatkozik. Ha több ilyen típus is létezik, a származtatott típuson belül deklarált típus lesz kiválasztva. Ez egy fordítóhiba, ha nincs olyan típus, amely származtatottabb lenne, mint az összes többi.

    Megjegyzés: Ha a T.I jelentését egy T típus esetén határozzák meg az alaposztály specifikációjának T feloldása során, akkor T közvetlen alaposztályának object tekintendő (15.2.4.2. §). végjegyzet

  • Ellenkező esetben a namespace_or_type_name érvénytelen, és fordítási időhiba lép fel.

A namespace_or_type_name feloldása az utolsó ismétlés feloldása. Rₖ

A namespace_or_type_name csak akkor hivatkozhat statikus osztályra (15.2.2.4. §), ha

  • A namespace_or_type_name az T, vagy
  • A namespace_or_type_name az űrlap T (12.8.18. §) typeof(T)

Példa:

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
}

A fenti példában a befelé irányuló NestedClass.M() hívás nem egyértelmű a kettő közöttC.Test(), és B.NestedClass.M() mivel egyik sem származtatottabb, mint a A.NestedClass.M() másik. A kétértelműség feloldásához explicit hivatkozás vagy A.NestedClass.M()B.NestedClass.M() szükséges.

záró példa

7.8.2 Nem minősített nevek

Minden névtér-deklaráció és típusdeklaráció neve a következőképpen van meghatározva:

  • Névtérdeklaráció esetén a nem minősített név a deklarációban megadott qualified_identifier .
  • Type_parameter_list nélküli típusdeklaráció esetén a nem minősített név a deklarációban megadott azonosító.
  • A K típusparaméterrel rendelkező típusdeklaráció esetében a nem minősített név a deklarációban megadott azonosító , amelyet a generikus dimenzió specifikátor követ a K típusparaméterekhez (12.8.18).

7.8.3 Teljes név

Minden névtér és típusdeklaráció teljes névvel rendelkezik , amely egyedileg azonosítja a névteret vagy a típusdeklarációt a programon belül. A névtér vagy a nem minősített névvel N rendelkező típusdeklaráció teljes neve a következőképpen határozható meg:

  • Ha N a globális névtér tagja, akkor a teljes neve N.
  • Ellenkező esetben a teljes neve S.Nannak S a névtérnek vagy típusdeklarációnak a teljes neve, amelyben N deklarálva van.

Más szóval a teljes név N az azonosítók és generic_dimension_specifierteljes hierarchikus elérési útja, amely Na globális névtérből indul ki. Mivel egy névtér vagy típus minden tagjának egyedi névvel kell rendelkeznie, a névtér vagy típusdeklaráció teljes neve mindig egyedi. Fordítási időhiba, hogy ugyanaz a teljes név két különálló entitásra hivatkozik. Elsősorban:

  • A névtér-deklaráció és a típusdeklaráció esetében is hiba, hogy ugyanazzal a teljes névvel rendelkezik.
  • Hiba, hogy két különböző típusdeklaráció esetében ugyanaz a teljes név van megszabva (például ha a szerkezet és az osztály deklarációja is ugyanazzal a teljes névvel rendelkezik).
  • Hiba, ha a részleges módosító nélküli típusdeklaráció teljes neve megegyezik egy másik típusdeklarációval (15.2.7. §).

Példa: Az alábbi példa több névteret és típusdeklarációt mutat be a hozzájuk tartozó teljes névvel együtt.

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

záró példa

7.9 Automatikus memóriakezelés

A C# automatikus memóriakezelést alkalmaz, amely lehetővé teszi a fejlesztők számára az objektumok által elfoglalt memória manuális kiosztását és felszabadítását. Az automatikus memóriakezelési szabályzatokat egy szemétgyűjtő valósítja meg. Egy objektum memóriakezelési életciklusa a következő:

  1. Az objektum létrehozásakor a rendszer lefoglalja a memóriát, futtatja a konstruktort, és élőnek tekinti az objektumot.
  2. Ha sem az objektum, sem a példánymezői nem érhetők el a végrehajtás bármely lehetséges folytatásával, a véglegesítők futtatásán kívül, az objektum már nem használható , és jogosulttá válik a véglegesítésre.

    Megjegyzés: A C#-fordító és a szemétgyűjtő dönthet úgy, hogy kódot elemez, hogy meghatározza, mely objektumokra mutató hivatkozásokat használhatja a jövőben. Ha például egy hatókörben lévő helyi változó az egyetlen létező hivatkozás egy objektumra, de a helyi változóra soha nem hivatkozik a végrehajtás bármely lehetséges folytatása az eljárás aktuális végrehajtási pontjáról, a szemétgyűjtő esetleg (de nem kötelező) már nem használja az objektumot. végjegyzet

  3. Miután az objektum jogosult a véglegesítésre, bizonyos meghatározott későbbi időpontokban az objektum véglegesítője (15.13.§) (ha van ilyen) fut. Normál körülmények között az objektum véglegesítője csak egyszer fut, bár a implementáció által definiált API-k lehetővé tehetik ennek a viselkedésnek a felülbírálását.
  4. Az objektum véglegesítőjének futtatása után, ha sem az objektum, sem a példánymezők nem érhetők el a végrehajtás bármely lehetséges folytatásával, beleértve a véglegesítők futtatását is, az objektum elérhetetlennek minősül, és az objektum jogosulttá válik a gyűjteményre.

    Megjegyzés: A korábban nem elérhető objektumok a véglegesítő miatt ismét akadálymentessé válhatnak. Erre alább talál egy példát. végjegyzet

  5. Végül, miután az objektum jogosulttá válik a gyűjteményre, a szemétgyűjtő felszabadítja az objektumhoz társított memóriát.

A szemétgyűjtő fenntartja az objektumhasználattal kapcsolatos információkat, és ezeket az információkat memóriakezelési döntések meghozatalára használja, például hogy hol található a memóriában egy újonnan létrehozott objektum, mikor kell áthelyezni egy objektumot, és ha egy objektum már nincs használatban vagy elérhetetlen.

A többi olyan nyelvhez hasonlóan, amelyek feltételezik a szemétgyűjtő létezését, a C# úgy van kialakítva, hogy a szemétgyűjtő számos memóriakezelési szabályzatot implementáljon. A C# nem határoz meg időkorlátot az adott időtartamon belül, sem a véglegesítők futtatásának sorrendjét. Azt, hogy a véglegesítők az alkalmazásleállítás részeként futnak-e, implementálás határozza meg (7.2.§).

A szemétgyűjtő viselkedése bizonyos mértékig szabályozható statikus módszerekkel az osztályon System.GC. Ezzel az osztálysal gyűjteményt kérhet le, véglegesítőket futtathat (vagy nem futtathat), és így tovább.

Példa: Mivel a szemétgyűjtő széles szélességet engedélyez az objektumok gyűjtésének és a véglegesítők futtatásának eldöntésében, a megfelelő megvalósítás olyan kimenetet eredményezhet, amely eltér az alábbi kódban láthatótól. A 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();
    }
}

létrehoz egy osztálypéldányt A és egy osztálypéldányt B. Ezek az objektumok akkor lesznek jogosultak a szemétgyűjtésre, ha a változóhoz b hozzá van rendelve az érték null, mivel ez után nem lehet elérni őket a felhasználó által írt kódokkal. A kimenet lehet

Finalize instance of A
Finalize instance of B

vagy

Finalize instance of B
Finalize instance of A

mivel a nyelv nem ír elő korlátozást az objektumok szemétgyűjtési sorrendjére.

Finom esetekben a "véglegesítésre jogosult" és a "gyűjtési jogosultság" közötti különbség fontos lehet. Például,

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");
        }
    }
}

A fenti programban, ha a szemétgyűjtő úgy dönt, hogy az utolsó előtti véglegesítőt A futtatja B, akkor a program kimenete a következő lehet:

Finalize instance of A
Finalize instance of B
A.F
RefA is not null

Vegye figyelembe, hogy bár a példány A nem volt használatban, és Aa véglegesítő futtatása megtörtént, továbbra is lehetséges, hogy a metódusokat A (ebben az esetben F) meghívják egy másik döntőstől. Azt is vegye figyelembe, hogy a véglegesítő futtatása azt eredményezheti, hogy egy objektum ismét használhatóvá válik a fővonal programból. Ebben az esetben a véglegesítő futtatása Bmiatt a korábban nem használt példány A elérhetővé vált az élő referenciából Test.RefA. A hívás WaitForPendingFinalizersután a példány B jogosult a gyűjteményre, de a hivatkozás Amiatt a példány Test.RefA nem.

záró példa

7.10 Végrehajtási végzés

A C#-programok végrehajtása úgy folytatódik, hogy az egyes végrehajtási szálak mellékhatásai megmaradnak a kritikus végrehajtási pontokon. A mellékhatásokat egy illékony mező olvasása vagy írása, egy nem felejtő változóba való írás, egy külső erőforrásba való írás és egy kivétel kiírása határozza meg. A kritikus végrehajtási pontok, amelyeknél a mellékhatások sorrendjét meg kell őrizni, az illékony mezőkre (15.5.4.§), lock utasításokra (13.13.§) és szállétrehozásra és -megszakításra való hivatkozás. A végrehajtási környezet szabadon módosíthatja a C#-programok végrehajtási sorrendjét, az alábbi korlátozásokra is figyelemmel:

  • Az adatfüggőség megmarad egy végrehajtási szálon belül. Vagyis az egyes változók értékét úgy számítja ki a rendszer, mintha a szál összes utasítása eredeti programsorrendben lett végrehajtva.
  • Az inicializálási rendezési szabályok megmaradnak (§15.5.5, §15.5.6).
  • A mellékhatások sorrendje megmarad az illékony olvasás és írás tekintetében (15.5.4. §). Emellett a végrehajtási környezetnek nem kell kiértékelnie egy kifejezés egy részét, ha arra következtethet, hogy a kifejezés értéke nincs használatban, és nincs szükség mellékhatásokra (beleértve a metódus meghívása vagy egy változó mező elérése által okozott mellékhatásokat). Ha a program végrehajtását egy aszinkron esemény szakítja meg (például egy másik szál által okozott kivétel), nem garantált, hogy a megfigyelhető mellékhatások az eredeti programrendben láthatók.