Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
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
asyncmódosítóval, feltéve, hogy a metódus visszatérési típusa vagySystem.Threading.Tasks.TaskSystem.Threading.Tasks.Task<int>. - A visszatérési típusnak a következőnek kell lennie
void: ,intSystem.Threading.Tasks.TaskvagySystem.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
asyncpontosan a fent megadott két visszatérési típus egyikével kell rendelkezniük ahhoz, hogy belépési pontnak minősüljenek. Egyasync voidmetódus, vagy egyasyncmásik várt típust visszaadó metódus, példáulValueTaskValueTask<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
Mainmetódus visszatérési típusa azSystem.Threading.Tasks.Task, a szintetizált metódus visszatérési típusa a következő:void - Ha a
Mainmetódus visszatérési típusa azSystem.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
Mainmetódust, és argumentumként adja át astring[]paraméter értékét, ha aMainmetódus rendelkezik ilyen paraméterrel. - Ha a
Mainmetó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,inta visszaadott értékGetResult()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ó
yaz 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óznincs 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ésMegacorp.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
FGmetódusok fordítási időt eredményeznek, mivel a névia külső blokkban deklarálva van, és a belső blokkban nem módosítható újra. A metódusok azonbanHérvényesek,Imivel a kétinem 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
objecttagjai szigorúan nem tagjai semmilyen felületnek (19.4. §). Az osztályobjecttagjai 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
publicmódosítót a tagdeklarációba. Az intuitív jelentésepublic"hozzáférés nem korlátozott". - Védett, amelyet úgy választunk ki, hogy beleveszünk egy
protectedmódosítót a tagdeklarációba. Az intuitív jelentés "protecteda 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
internalmódosítót a tagdeklarációba. Az intuitív jelentéseinternal"hozzáférés korlátozott ehhez a szerelvényhez". - Védett belső, amely úgy van kiválasztva, hogy egy és egy
protectedmódosító isinternalszerepel a tag deklarációjában. Az intuitív jelentéseprotected 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 egyprotectedmódosító is szerepel. Az intuitív jelentés "private protectedelé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
privatemódosítót a tagdeklarációba. Az intuitív jelentéseprivate"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
publicdeklarálhatnakinternalakadálymentességet, és alapértelmezés szerintinternala 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
privateis 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
publicvagyinternaldeklarált akadálymentességgel rendelkezhetnek. végjegyzet - A strukturált szerkezet tagjai a deklarált akadálymentességhez tartozhatnak,
publicvagyinternaldeklarálhatjákprivateaz akadálymentességet, és alapértelmezés szerintprivatea deklarált akadálymentességet, mivel a szerkezetek implicit módon lezárva vannak. A szerkezetbenstructbevezetett (azaz nem az adott struktúra által örökölt) tagokat nem lehetprotected,protected internalilletveprivate protectednem deklarált akadálymentesség.Megjegyzés: A struktúra tagjaként deklarált típus lehet
public,internalvagyprivatedeklarált akadálymentesség, míg egy névtér tagjaként deklarált típus csakpublicvagyinternaldeklará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
Tnyilvános, akkor az akadálymentességi tartományTa program szövegePés bármely olyan program, amely hivatkozikPrá. - Ha a deklarált akadálymentesség
Tbelső, az akadálymentességi tartományTa program szövegeP.
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
Mazpublic, akkor az akadálymentességi tartományMaz akadálymentességiTtartomány. - Ha a deklarált akadálymentesség
Mazprotected internal, akkor legyenDa program szövegénekPés bármilyen származtatott programszövegnek az egyesítése , amely kívülTPvan deklarálva . Az akadálymentességiMtartomány az akadálymentességi tartomány metszeteTD. - Ha a deklarált akadálymentesség
Mazprivate protected, akkor legyenDa program szövegének és a program szövegének metszetePT, illetve bármilyen, a programbólTszármaztatott típus. Az akadálymentességiMtartomány az akadálymentességi tartomány metszeteTD. - Ha a deklarált akadálymentesség az , akkor legyen
Ma program szövegénekprotectedés a program szövegének egyesítését bármilyen típusú származtatottD.TTAz akadálymentességiMtartomány az akadálymentességi tartomány metszeteTD. - Ha a deklarált akadálymentesség
Mazinternal, akkor az akadálymentességi tartományMaz akadálymentességi tartomány és a program szövegénekTmetszeteP. - Ha a deklarált akadálymentesség
Mazprivate, akkor az akadálymentességi tartományMa program szövegeT.
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
Mfé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
Megy 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 igen
M,publicakkor 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, amelybenMdeklarálva van, vagy ha az a deklarált osztályból származtatott osztályonMbelü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ályonMbelül történik, vagy ha a deklarált osztályból származtatott osztályonMbelül történik, és a származtatott osztálytípuson keresztül történik (7.5.4. §).MinternalEllenkező esetben a hozzáférés akkor engedélyezett, ha az abban a programban történik, amelybenMdeklarálva van.MprivateEllenkező esetben a hozzáférés akkor engedélyezett, ha a deklarált típusonMbelü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.Xkorlátlan.- A ( ,
A.Y,B,B.X,B.YB.C) akadálymentességi tartományaB.C.X, ésB.C.Yaz azt tartalmazó program programszövege.- Az akadálymentességi
A.Ztartomány a program szövegeA.- Az akadálymentességi tartomány
B.ZB.Dés a program szövegeB, beleértve az ésB.Ca program szövegétB.Dis.- Az akadálymentességi
B.C.Ztartomány a program szövegeB.C.- Az akadálymentességi tartomány
B.D.XB.D.Yés a program szövegeB, beleértve az ésB.Ca program szövegétB.Dis.- Az akadálymentességi
B.D.Ztartomány a program szövegeB.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 mindenXtag rendelkezik nyilvánosan deklarált akadálymentességgel, azA.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
Bosztály örökli a privát tagotxazAosztálytól. Mivel a tag privát, csak a class_bodyAérhető el. Így a hozzáférésb.xsikeres aA.Fmetódusban, de a metódusbanB.Fmeghiú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.Maz osztály típusaEvagy az abból származtatottTosztály, aholTaz osztályT, vagy egy osztálytípus, amelybőlDké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ányonxkeresztül lehet hozzáférniA, mivelBmindkét esetben a hozzáférés egy példányon vagy egy, a forrásbólAszármaztatott osztályon keresztülA. A rendszeren belülBazonban nem lehet hozzáférnixegy példányonAkeresztül , mivelAnem származik belőleB.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
xengedé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>.xDvaló hivatkozás akkor is érvényes, ha az osztályDa következőbőlC<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
Bosztály fordítási időt eredményez, mertAlegalább nem olyan elérhető, mint aB.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
HmetódusBfordítási időt eredményez, mert a visszatérési típusAnem 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
paramsparamé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
paramsjobb 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
paramsmegfelelő 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ésref. 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ésrefparamétermódosító (§15.6.2) az aláírás része. Így,F(int),F(in int),F(out int)ésF(ref int)mind egyedi aláírások. Azonban ,F(in int)F(out int)ésF(ref int)nem deklarálható ugyanazon a felületen belül, mert aláírásaik kizárólag ain,outésref. Azt is vegye figyelembe, hogy a visszatérési típus és aparamsmó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 aparamsmódosító felvétele vagy kizárása alapján. Ezért a módszerekF(int)deklarációi ésF(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
forA 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 hivatkozzonirá.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 } }
FA fenti módszerben az első hozzárendelésinem 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.GA metódusban a deklarátorjinicializálóban való használatajazért érvényes, mert a használat nem előzi meg a deklarátort. A metódusban aHké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
Aegy kifejezéskörnyezetben a helyi változóraAhivatkozik, és egy típuskörnyezetben az osztályraAhivatkozik.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
Fpéldányváltozótielrejti a helyi változói,Gde aimetóduson belül továbbra is a példányváltozóra hivatkozik. A helyi függvényenM1belül afloat iközvetlen külsői. A lambda paraméterielrejti afloat ilambda 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 aFdeklaráltatInner, mert az összes külső előfordulástFelrejti a belső deklaráció. Ugyanebből az okból a hívásF("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ó
FDerivedfigyelmezteté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óBaseolyan metódust vezetettFbe, 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
newmódosító azt jelzi, hogy aFbe "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
Faz in deklaráció elrejtiDerivedazFörököltetBase, de mivel az újnakFDerivedprivát hozzáférése van, hatóköre nem terjed ki a következőreMoreDerived: . Így a betárcsázásF()MoreDerived.Gérvényes, és meghívjaBase.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:
II<A₁, ..., Aₓ>
ahol:
-
Iegyetlen azonosító; és -
<A₁, ..., Aₓ>egy típus_argumentum_lista, ha nincs megadva típus_argumentum_lista, akkor tekintsükxnullának.
R₀ meghatározása az alábbiak szerint történik:
- Ha
xnulla, é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, akkorR₀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
xnulla, és a deklarációTegy névvelIrendelkező típusparamétert tartalmaz, akkorR₀az adott típusparaméterre hivatkozik. - Ellenkező esetben, ha a namespace_or_type_name a típusdeklaráció törzsében jelenik meg, vagy
Tbármely alaptípusa tartalmaz egy beágyazott akadálymentes típust, amelynek neveIésxtípusparaméterei vannak, akkorR₀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
xnulla, ésIa névtér neve a következőbenNvan:- 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
Nvagy using_alias_directive, amely a nevet egy névtérhez vagy típushoz társítja, akkor aInem egyértelmű, és fordítási időhiba lép fel. - Ellenkező esetben a
R₀utal aI-ben találhatóNnevű névtérre.
- 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
- Ellenkező esetben, ha
Negy névvelIésxtípusparaméterekkel rendelkező akadálymentes típust tartalmaz, akkor:- Ha
xnulla, és a namespace_or_type_name helyét egy névtér deklarációja tartalmazza, és a névtér-deklarációNtartalmaz egy extern_alias_directive vagy using_alias_directive , amely a nevetIegy 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.
- Ha
- Ellenkező esetben, ha a namespace_or_type_name helye a következő névtérdeklarációval
Nvan elzárva:- Ha
xnulla, és a névtér-deklaráció tartalmaz egy extern_alias_directive vagy using_alias_directive , amely a nevetIegy importált névtérhez vagy típushoz társítja, akkorR₀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ésxtípusparamétert tartalmazó típust tartalmaznak, akkorR₀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ésxtípusparamétert tartalmazó típust tartalmaznak, akkor a namespace_or_type_name nem egyértelmű, és fordítási időhiba lép fel.
- Ha
- Ha
- Ellenkező esetben a namespace_or_type_name nincs meghatározva, és fordítási időhiba lép fel.
- Ha
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
xnulla, ésRₚegy névtérre hivatkozik, ésRₚegy névvelIellátott beágyazott névteret tartalmaz, akkorRₙa beágyazott névtérre hivatkozik. - Ellenkező esetben, ha
Rₚegy névtérre hivatkozik, ésRₚegy név-Iésxtípusparaméterekkel rendelkező akadálymentes típust tartalmaz, akkorRₙ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, ésRₚvagy bármely alaptípusa tartalmaz egy beágyazott akadálymentes típust, amelynek neveIésxtípusparaméterei vannak, akkorRₙ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.Ijelentését egyTtípus esetén határozzák meg az alaposztály specifikációjánakTfeloldása során, akkorTközvetlen alaposztályánakobjecttekintendő (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(), ésB.NestedClass.M()mivel egyik sem származtatottabb, mint aA.NestedClass.M()másik. A kétértelműség feloldásához explicit hivatkozás vagyA.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
Na globális névtér tagja, akkor a teljes neveN. - Ellenkező esetben a teljes neve
S.NannakSa névtérnek vagy típusdeklarációnak a teljes neve, amelybenNdeklará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ő:
- Az objektum létrehozásakor a rendszer lefoglalja a memóriát, futtatja a konstruktort, és élőnek tekinti az objektumot.
- 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
- 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.
- 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
- 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ánytB. Ezek az objektumok akkor lesznek jogosultak a szemétgyűjtésre, ha a változóhozbhozzá van rendelve az értéknull, mivel ez után nem lehet elérni őket a felhasználó által írt kódokkal. A kimenet lehetFinalize instance of A Finalize instance of Bvagy
Finalize instance of B Finalize instance of Amivel 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
AfuttatjaB, akkor a program kimenete a következő lehet:Finalize instance of A Finalize instance of B A.F RefA is not nullVegye figyelembe, hogy bár a példány
Anem volt használatban, ésAa véglegesítő futtatása megtörtént, továbbra is lehetséges, hogy a metódusokatA(ebben az esetbenF) 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ásaBmiatt a korábban nem használt példányAelérhetővé vált az élő referenciábólTest.RefA. A hívásWaitForPendingFinalizersután a példányBjogosult a gyűjteményre, de a hivatkozásAmiatt a példányTest.RefAnem.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.
ECMA C# draft specification