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.
13.1 Általános
A C# számos utasítást tartalmaz.
Megjegyzés: A legtöbb ilyen állítás ismerős lesz azoknak a fejlesztőknek, akik C és C++ nyelven programoztak. végjegyzet
statement
: labeled_statement
| declaration_statement
| embedded_statement
;
embedded_statement
: block
| empty_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
| try_statement
| checked_statement
| unchecked_statement
| lock_statement
| using_statement
| yield_statement
| unsafe_statement // unsafe code support
| fixed_statement // unsafe code support
;
unsafe_statement (24.2. §) és fixed_statement (24.7. §) csak nem biztonságos kódban (24. §) érhető el.
A embedded_statement nemterminális a többi utasításban megjelenő utasításokhoz használatos. A embedded_statement utasítás helyetthasználata kizárja a deklarációs utasítások és a címkézett utasítások használatát ezekben a kontextusokban.
Példa: A kód
void F(bool b) { if (b) int i = 44; }fordítási idejű hibát eredményez, mert egy
ifutasításnál a ághoz egy beágyazott utasításra van szükség, nem pedig egy utasításra. Ha ez a kód engedélyezve lenne, akkor a változóideklarálva lesz, de soha nem használható. Azonban vegye figyelembe, hogy a deklaráció blokkon belüli elhelyezésével aipélda érvényes.záró példa
13.2 Végpontok és elérhetőség
Minden utasításnak van egy végpontja. Intuitív értelemben az utasítás végpontja az a hely, amely azonnal követi az utasítást. Az összetett utasítások végrehajtási szabályai (beágyazott utasításokat tartalmazó utasítások) határozzák meg azt a műveletet, amelyet akkor hajtanak végre, amikor a vezérlő eléri a beágyazott utasítás végpontját.
Példa: Amikor a vezérlőelem eléri egy blokkban lévő utasítás végpontját, a vezérlő a blokk következő utasítására kerül át. záró példa
Ha a végrehajtással el lehet érni egy utasítást, akkor a rendszer azt mondja, hogy elérhető. Ezzel szemben, ha nincs lehetőség az utasítás végrehajtására, a rendszer azt mondja, hogy az utasítás nem érhető el.
Példa: Az alábbi kódban
void F() { Console.WriteLine("reachable"); goto Label; Console.WriteLine("unreachable"); Label: Console.WriteLine("reachable"); }A Console.WriteLine második meghívása nem érhető el, mert nincs lehetőség az utasítás végrehajtására.
záró példa
Figyelmeztetést adunk, ha egy throw_statement, blokk vagy empty_statement kivételével bármilyen utasítás nem érhető el. Kifejezetten nem hiba, ha egy állítás nem érhető el.
Megjegyzés: Annak meghatározásához, hogy egy adott utasítás vagy végpont elérhető-e, a fordító az egyes utasításokhoz meghatározott elérhetőségi szabályok szerint végzi a folyamatelemzést. A folyamatelemzés figyelembe veszi az utasítások viselkedését szabályozó állandó kifejezések (12.25.§) értékeit, de a nem állandó kifejezések lehetséges értékeit nem veszi figyelembe. Más szóval, vezérlési folyamat elemzés céljából egy adott típus nem állandó kifejezése bármilyen lehetséges értékkel rendelkezhet.
A példában
void F() { const int i = 1; if (i == 2) Console.WriteLine("unreachable"); }az utasítás logikai kifejezése
ifállandó kifejezés, mivel az==operátor mindkét operandusa állandó. Mivel az állandó kifejezés fordítási időpontban van kiértékelve, és az értéketfalseállítja elő, aConsole.WriteLinemeghívás elérhetetlennek minősül. Haiazonban helyi változóként van módosítvavoid F() { int i = 1; if (i == 2) Console.WriteLine("reachable"); }a
Console.WriteLinemeghívás elérhetőnek tekinthető, annak ellenére, hogy a valóságban soha nem hajtják végre.végjegyzet
A függvénytagok vagy névtelen függvények blokkja mindig elérhetőnek tekinthető. A blokkokban lévő egyes állítások elérhetőségi szabályainak egymást követő kiértékelésével bármely adott utasítás elérhetősége meghatározható.
Példa: Az alábbi kódban
void F(int x) { Console.WriteLine("start"); if (x < 0) Console.WriteLine("negative"); }a második
Console.WriteLineelérhetőségét az alábbiak szerint határozzuk meg:
- Az első
Console.WriteLinekifejezési utasítás azért érhető el, mert aFmetódus blokkja elérhető (13.3. §).- Az első
Console.WriteLinekifejezési utasítás végpontja azért érhető el, mert ez az utasítás elérhető (13.7. és 13.3. §).- Az
ifutasítás azért érhető el, mert az elsőConsole.WriteLinekifejezési utasítás végpontja elérhető (§13.7 és §13.3).- A második
Console.WriteLinekifejezési utasítás azért érhető el, mert az utasítás logikai kifejezésénekifnincs állandó értékefalse.záró példa
Két olyan helyzet van, amikor egy utasítás végpontjának fordítási idő hibája érhető el:
Mivel az
switchutasítás nem engedi meg, hogy egy váltószakasz „átlépjen” a következő váltószakaszra, fordítási idejű hiba, ha egy váltószakasz utasításlistájának végpontja elérhető. Ha ez a hiba jelentkezik, az általában azt jelzi, hogy hiányzik egybreakutasítás.Ez egy függvénytag blokkjának végpontjára vagy egy névtelen függvényre vonatkozó fordítási időhiba, amely egy elérhető értéket számít ki. Ha ez a hiba jelentkezik, az általában azt jelzi, hogy hiányzik egy
returnutasítás (13.10.5. §).
13.3 Blokkok
13.3.1 Általános
A blokkok lehetővé teszik több utasítás írását olyan környezetekben, ahol egyetlen utasítás engedélyezett.
block
: '{' statement_list? '}'
;
Egy blokk egy opcionális statement_list (§13.3.2), kapcsos zárójelek közé van zárva. Ha az utasításlista nincs megadva, a blokk üresnek tekintendő.
A blokk tartalmazhat deklarációs utasításokat (13.6. §). A blokkban deklarált helyi változó vagy állandó hatóköre a blokk.
A blokkok a következőképpen lesznek végrehajtva:
- Ha a blokk üres, a vezérlő a blokk végpontjára kerül.
- Ha a blokk nem üres, a vezérlő átkerül az utasításlistára. Ha a vezérlőelem eléri az utasításlista végpontját, a vezérlő a blokk végpontjára kerül.
A blokkok utasításlistája akkor érhető el, ha maga a blokk elérhető.
A blokk végpontja akkor érhető el, ha a blokk üres, vagy ha az utasításlista végpontja elérhető.
Egy blokk, amely egy vagy több yield utasítást tartalmaz (§13.15), iterátorblokknak nevezünk. Az iterátorblokkok a függvénytagok iterátorként való implementálására szolgálnak (15.15.§). Az iterátorblokkokra további korlátozások vonatkoznak:
- Fordítási idejű hiba, ha egy
returnutasítás megjelenik egy iterátorblokkban (deyield returnutasítások engedélyezettek). - Fordítási idő hibája, ha egy iterátorblokk nem biztonságos környezetet tartalmaz (24.2. §). Az iterátorblokkok mindig meghatároznak egy biztonságos környezetet, még akkor is, ha a deklarációja nem biztonságos környezetben van beágyazva.
13.3.2 Nyilatkozatlisták
Az utasításlista egy vagy több egymás után írt utasításból áll. Az utasításlisták az s blokkokban (13.3. §) és switch_blocks-ben (13.8.3. §) fordulnak elő.
statement_list
: statement+
;
Az utasításlistát úgy hajtja végre a rendszer, hogy a vezérlőt az első utasításnak átadja. Amikor és ha a vezérlés eléri az utasítás végpontját, a vezérlés továbbítódik a következő utasításhoz. Ha a vezérlőelem eléri az utolsó utasítás végpontját, a vezérlőelem átkerül az utasításlista végpontjára.
Az utasításlistában szereplő utasítás akkor érhető el, ha az alábbiak közül legalább az egyik igaz:
- Az utasítás az első utasítás, és maga az utasításlista elérhető.
- Az előző utasítás végpontja elérhető.
- Az utasítás egy címkézett utasítás, a címkére pedig egy elérhető
gotoutasítás hivatkozik.
Az utasításlista végpontja akkor érhető el, ha a lista utolsó utasításának végpontja elérhető.
13.4 Az üres utasítás
Egy empty_statement nem csinál semmit.
empty_statement
: ';'
;
Üres utasítást akkor használ a rendszer, ha nincs olyan környezetben végrehajtandó művelet, amelyben utasításra van szükség.
Az üres utasítás végrehajtása egyszerűen átviszi az irányítást az utasítás végpontjára. Így az üres utasítás végpontja akkor érhető el, ha az üres utasítás elérhető.
Példa: Üres utasítás használható null törzsű utasítás írásakor
while:bool ProcessMessage() {...} void ProcessMessages() { while (ProcessMessage()) ; }Üres utasítással is deklarálhat egy címkét a blokk záró "
}" előtt:void F(bool done) { ... if (done) { goto exit; } ... exit: ; }záró példa
13.5 Címkézett utasítások
A labeled_statement lehetővé teszi, hogy egy utasítást címkével előtaggal lássanak el. A címkézett utasítások blokkokban engedélyezettek, beágyazott utasításként azonban nem engedélyezettek.
labeled_statement
: identifier ':' statement
;
A címkézett utasítás az azonosító által megadott névvel deklarál egy címkét. A címke hatóköre az a teljes blokk, amelyben a címke deklarálva van, beleértve a beágyazott blokkokat is. Két azonos nevű címke hatókörei átfedésben vannak, kompilálási hibát okoz.
A címkére a goto utasításokból lehet hivatkozni a címke hatókörén belül (§13.10.4).
Megjegyzés: Ez azt jelenti, hogy az
gotoutasítások blokkokban és blokkokból is átvihetik a vezérlést, de soha nem blokkokba. végjegyzet
A címkék saját deklarációs területtel rendelkeznek, és nem zavarják más azonosítókat.
Példa: A példa
int F(int x) { if (x >= 0) { goto x; } x = -x; x: return x; }érvényes, és az x nevet használja paraméterként és címkeként is.
záró példa
A címkézett utasítás végrehajtása pontosan megfelel a címkét követő utasítás végrehajtásának.
A normál vezérlési folyamat által biztosított elérhetőség mellett a címkézett utasítás akkor érhető el, ha a címkére egy elérhető goto utasítás hivatkozik, kivéve, ha az goto utasítás egy try blokkban vagy egy catch a try_statement olyan blokkjában található, amely tartalmaz egy finally olyan blokkot, amelynek végpontja elérhetetlen, és a címkézett utasítás kívül esik a try_statement.
13.6 Nyilatkozatok
13.6.1 Általános
Egy declaration_statement deklarál egy vagy több helyi változót, egy vagy több helyi állandót vagy helyi függvényt. A deklarációs utasítások blokkokban és kapcsolóblokkokban engedélyezettek, beágyazott utasításokként azonban nem.
declaration_statement
: local_variable_declaration ';'
| local_constant_declaration ';'
| local_function_declaration
;
A helyi változók deklarálása local_variable_declaration használatával történik (13.6.2. §). A helyi állandók local_constant_declaration használatával deklarálhatók (13.6.3. §). A helyi függvények deklarálása local_function_declaration használatával történik (13.6.4. §).
A deklarált nevek a legközelebbi deklarációs területre kerülnek be (§7.3).
13.6.2 Helyi változódeklarációk
13.6.2.1 Általános
Egy local_variable_declaration deklarál egy vagy több helyi változót.
local_variable_declaration
: implicitly_typed_local_variable_declaration
| explicitly_typed_local_variable_declaration
| explicitly_typed_ref_local_variable_declaration
;
Az implicit módon beírt deklarációk tartalmazzák a környezetfüggő kulcsszót (6.4.4.4. §), var amely szintaktikai kétértelműséget eredményez a három kategória között, amely az alábbiak szerint van megoldva:
- Ha nincs névvel ellátott
vartípus a hatókörben, és a bemenet megfelel implicitly_typed_local_variable_declaration, akkor azt választják ki. - Ellenkező esetben, ha egy névvel ellátott
vartípus a hatókörben van, akkor a implicitly_typed_local_variable_declaration nem tekinthető lehetséges egyezésnek.
Egy helyi_változó_deklaráció minden változót egy deklarátor vezet be, amely vagy impliciten_típusú_helyi_változó_deklarátor, explicit_típusú_helyi_változó_deklarátor, vagy referencia_helyi_változó_deklarátor az impliciten típusú, explicit típusú és referenciával ellátott helyi változók esetében. A deklarátor határozza meg a bevezetett változó nevét (azonosítóját) és kezdeti értékét, ha van ilyen.
Ha egy deklarátor több deklarátort is tartalmaz, akkor azok feldolgozása, beleértve az inicializáló kifejezéseket is, balról jobbra haladva történik (9.4.4.5. §).
Megjegyzés: Ha egy local_variable_declaration nem for_initializer (13.9.4. §) vagy resource_acquisition (13.14. §) fordul elő, ez a balról jobbra sorrend megegyezik minden deklarátornak egy külön local_variable_declaration belül. Példa:
void F() { int x = 1, y, z = x * 2; }egyenértékű:
void F() { int x = 1; int y; int z = x * 2; }végjegyzet
A helyi változó értékét egy simple_name (12.8.4. §) használó kifejezésben nyeri ki. A helyi változót mindenképpen ki kell osztani (9.4. §) minden olyan helyen, ahol az értéket megkapják. Az local_variable_declaration által bevezetett helyi változók kezdetben nem lesznek hozzárendelve (9.4.3. §). Ha egy deklarátor inicializáló kifejezéssel rendelkezik, akkor a bevezetett helyi változó a deklarátor végén hozzárendeltként lesz besorolva (9.4.4.5. §).
A local_variable_declaration által bevezetett helyi változó hatóköre a következőképpen van definiálva (7.7.§):
- Ha a deklaráció egy for_initializer, akkor a hatókör a for_initializer, a for_condition, a for_iterator és a embedded_statement (13.9.4. §);
- Ha a deklaráció resource_acquisition, akkor a hatókör a using_statement szemantikailag egyenértékű kiterjesztésének legkülső blokkja ( §13.14).
- Ellenkező esetben a hatókör az a blokk, amelyben a deklaráció történik.
Hiba egy helyi változóra név szerint hivatkozni a deklarátort megelőző szöveges pozícióban vagy a deklarátoron belüli inicializáló kifejezésben. A helyi változó hatókörén belül fordítási időhiba egy másik helyi változó, helyi függvény vagy állandó azonos nevű deklarálása.
A ref lokális változó ref-safe-context-je (§9.7.2) az inicializáló variable_reference ref-safe-context-je. A nem ref helyi változók ref-safe kontextusa a deklarációblokk.
13.6.2.2 Implicit módon beírt helyi változódeklarációk
implicitly_typed_local_variable_declaration
: 'var' implicitly_typed_local_variable_declarator
| ref_kind 'var' ref_local_variable_declarator
;
implicitly_typed_local_variable_declarator
: identifier '=' expression
;
Egy implicitly_typed_local_variable_declaration egyetlen helyi változót, azonosítót vezet be. A kifejezés vagy változó hivatkozás rendelkezzen fordítási idő típusával, T. Az első alternatíva egy kifejezés kezdeti értékével rendelkező változót deklarál, amelynek típusa T? nem T null értékű hivatkozástípus, ellenkező esetben a típusa .T A második alternatíva egy ref változót deklarál, amelynek kezdeti értéke refvariable_reference; a típus ref T?, ha T nem null-hivatkozás típus, egyébként a típus ref T. (ref_kind a §15.6.1 le van írva.)
Példa:
var i = 5; var s = "Hello"; var d = 1.0; var numbers = new int[] {1, 2, 3}; var orders = new Dictionary<int,Order>(); ref var j = ref i; ref readonly var k = ref i;A fenti implicit módon beírt helyi változódeklarációk pontosan egyenértékűek a következő explicit módon beírt deklarációkkal:
int i = 5; string s = "Hello"; double d = 1.0; int[] numbers = new int[] {1, 2, 3}; Dictionary<int,Order> orders = new Dictionary<int,Order>(); ref int j = ref i; ref readonly int k = ref i;Az alábbiak helytelen implicit módon beírt helyi változódeklarációk:
var x; // Error, no initializer to infer type from var y = {1, 2, 3}; // Error, array initializer not permitted var z = null; // Error, null does not have a type var u = x => x + 1; // Error, anonymous functions do not have a type var v = v++; // Error, initializer cannot refer to v itselfzáró példa
13.6.2.3 Explicit módon beírt helyi változódeklarációk
explicitly_typed_local_variable_declaration
: type explicitly_typed_local_variable_declarators
;
explicitly_typed_local_variable_declarators
: explicitly_typed_local_variable_declarator
(',' explicitly_typed_local_variable_declarator)*
;
explicitly_typed_local_variable_declarator
: identifier ('=' local_variable_initializer)?
;
local_variable_initializer
: expression
| array_initializer
;
Egy explicitly_typed_local_variable_declaration egy vagy több helyi változót vezet be a megadott típussal.
Ha egy local_variable_initializer jelen van, annak típusa az egyszerű hozzárendelés (12.23.2. §) vagy a tömb inicializálásának (17.7. §) szabályai szerint megfelelő, és az értéke a változó kezdeti értékeként van hozzárendelve.
13.6.2.4 Explicit módon beírt ref helyi változódeklarációk
explicitly_typed_ref_local_variable_declaration
: ref_kind type ref_local_variable_declarators
;
ref_local_variable_declarators
: ref_local_variable_declarator (',' ref_local_variable_declarator)*
;
ref_local_variable_declarator
: identifier '=' 'ref' variable_reference
;
Az inicializáló variable_referencetípustípussal kell rendelkeznie, és meg kell felelnie a hiv-hozzárendelésre vonatkozó követelményeknek (12.23.3. §).
Ha ref_kind van ref readonly, a deklarált azonosítókírásvédettként kezelt változókra hivatkoznak. Ellenkező esetben, ha ref_kind van ref, akkor a deklarált azonosítók írható változókra mutató hivatkozások.
Fordítási idő alatt hiba történik, ha egy ref típusú helyi változót vagy egy ref struct típusú változót deklarálunk egy method_modifierasync-el deklarált metóduson belül, vagy egy iterátoron belül (§15.15).
13.6.3 Helyi állandó deklarációk
Egy local_constant_declaration egy vagy több helyi állandót deklarál.
local_constant_declaration
: 'const' type constant_declarators
;
constant_declarators
: constant_declarator (',' constant_declarator)*
;
constant_declarator
: identifier '=' constant_expression
;
A local_constant_declaration típusahatározza meg a deklaráció által bevezetett állandók típusát. A típust a constant_declarator-eklistája követi, amelyek mindegyike új állandót vezet be. A constant_declarator az állandót elnevező azonosítóból , majd egy "=" jogkivonatból, majd egy constant_expression (12.25. §) áll, amely az állandó értékét adja.
A helyi állandó deklaráció típusa és constant_expression ugyanazokkal a szabályokkal kell rendelkeznie, mint egy állandó tagnyilatkozatnak (15.4. §).
A helyi állandó értékét egy simple_name (12.8.4. §) használó kifejezésben nyeri ki.
A helyi állandó hatóköre az a blokk, amelyben a deklaráció történik. Hiba, ha egy helyi állandóra olyan szöveges pozícióban hivatkozunk, amely megelőzi a konstans_deklarátor végét.
A több állandót deklaráló helyi állandó deklaráció egyenértékű az azonos típusú állandók több deklarációjával.
13.6.4 Helyi függvénydeklarációk
A local_function_declaration helyi függvényt deklarál.
local_function_declaration
: local_function_modifier* return_type local_function_header
local_function_body
| ref_local_function_modifier* ref_kind ref_return_type
local_function_header ref_local_function_body
;
local_function_header
: identifier '(' parameter_list? ')'
| identifier type_parameter_list '(' parameter_list? ')'
type_parameter_constraints_clause*
;
local_function_modifier
: ref_local_function_modifier
| 'async'
;
ref_local_function_modifier
: 'static'
| unsafe_modifier // unsafe code support
;
local_function_body
: block
| '=>' null_conditional_invocation_expression ';'
| '=>' expression ';'
;
ref_local_function_body
: block
| '=>' 'ref' variable_reference ';'
;
Nyelvtani megjegyzés: Amikor a local_function_body felismerése történik, ha mind a null_conditional_invocation_expression, mind a kifejezés alternatívái alkalmazhatók, akkor az előbbit kell választani. (15.6.1. §)
Példa: A helyi függvények két gyakori használati esete van: iterátor- és aszinkron metódusok. Az iterátor metódusokban minden kivétel csak a visszaadott sorozatot számba vevő kód meghívásakor figyelhető meg. Az aszinkron metódusokban a rendszer csak akkor figyeli meg a kivételeket, ha a visszaadott feladatra vár. Az alábbi példa azt mutatja be, hogy a paraméterérvényesítést el kell különíteni az iterátor implementációjától egy helyi függvény használatával:
public static IEnumerable<char> AlphabetSubset(char start, char end) { if (start < 'a' || start > 'z') { throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter"); } if (end < 'a' || end > 'z') { throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter"); } if (end <= start) { throw new ArgumentException( $"{nameof(end)} must be greater than {nameof(start)}"); } return AlphabetSubsetImplementation(); IEnumerable<char> AlphabetSubsetImplementation() { for (var c = start; c < end; c++) { yield return c; } } }záró példa
Ha az alábbiakban másként nem rendelkezik, az összes nyelvtani elem szemantikája megegyezik a metódus_deklaráció (15.6.1. §) szemantikájával, amelyet a metódus helyett egy helyi függvény kontextusában kell olvasni.
A local_function_declaration azonosítójának egyedinek kell lennie a deklarált blokk hatókörében, beleértve a helyi változó deklarációs szóközöket is. Ennek egyik következménye, hogy a túlterhelt local_function_declaration nem engedélyezett.
A local_function_declaration tartalmazhat egy async (15.14.)-módosító és egy unsafe (24.1. §)-módosító. Ha a deklaráció tartalmazza a async módosítót, akkor a visszatérési típusnak void-nek vagy «TaskType» típusnak kell lennie (15.14.1. §). Ha a deklaráció tartalmazza a static módosítót, akkor a függvény statikus helyi függvény, ellenkező esetben nem statikus helyi függvény. Fordítási időhibát okoz, ha a type_parameter_list vagy a parameter_list attribútumokat tartalmaz. Ha a helyi függvény nem biztonságos környezetben van deklarálva (24.2.§), a helyi függvény tartalmazhat nem biztonságos kódot, még akkor is, ha a helyi függvény deklarációja nem tartalmazza a unsafe módosítót.
A rendszer egy helyi függvényt deklarál a blokk hatókörében. A nem statikus helyi függvény rögzítheti a változókat a környezetéből, míg a statikus helyi függvény nem (így nem fér hozzá a környezetben lévő helyi változókhoz, paraméterekhez, nem statikus helyi függvényekhez vagy this). Fordítási idejű hiba, ha egy rögzített változót egy nem statikus helyi függvény törzse olvas be, de nem feltétlenül van hozzárendelve a függvény minden hívása előtt. A fordítónak meg kell határoznia, hogy mely változók lesznek egyértelműen hozzárendelve a visszatéréskor (§9.4.4.33).
Ha a típus this egy struktúratípus, akkor fordítási idejű hiba, ha a helyi függvény törzse hozzáfér this. Ez igaz, akár az explicit (mint this.x), akár az implicit hozzáférésre (mint amikor x egy példány tagja a x struktúrának). Ez a szabály csak az ilyen hozzáférést tiltja, és nem befolyásolja, hogy a tagok keresésének eredménye a struktúra egy tagja legyen.
Fordítási időhiba, hogy a helyi függvény törzse olyan utasítást goto , utasítást break vagy utasítást continue tartalmaz, amelynek célja a helyi függvény törzsén kívül esik.
Megjegyzés: a fenti szabályok
thisgotoa 12.21.3. végjegyzet
A helyi függvények a deklaráció előtt lexikális pontból hívhatók meg. Fordítási idejű hiba azonban, ha a függvényt lexikálisan a helyi függvényben használt változó deklarálása előtt deklarálják (7.7 §).
Egy helyi függvény fordítási idejű hibája, ha egy paramétert, típusparamétert vagy helyi változót olyan névvel deklarál, amely már egy külsőbb hatókörben lévő helyi változó deklaráció térben van deklarálva.
A helyi függvénytestek mindig elérhetők. A helyi függvénydeklaráció végpontja akkor érhető el, ha a helyi függvénydeklaráció kezdőpontja elérhető.
Példa: Az alábbi példában a törzs
Lakkor is elérhető, ha a kezdőpontLnem érhető el. Mivel a kezdőpontLnem érhető el, a végpontotLkövető utasítás nem érhető el:class C { int M() { L(); return 1; // Beginning of L is not reachable int L() { // The body of L is reachable return 2; } // Not reachable, because beginning point of L is not reachable return 3; } }Más szóval a helyi függvénydeklaráció helye nem befolyásolja a benne található utasítások elérhetőségét. záró példa
Ha a helyi függvény argumentumának típusa az dynamic, a meghívandó függvényt fordításkor kell feloldani, nem pedig futásidőben.
A kifejezésfában nem használhatók helyi függvények.
Statikus helyi függvény
- Hivatkozhat statikus tagokra, típusparaméterekre, állandó definíciókra és statikus helyi függvényekre a belefoglalt hatókörből.
- Nem hivatkozhat
thisvagybasenem hozhat létre példánytagokat implicitthishivatkozásból, sem helyi változókat, paramétereket vagy nem statikus helyi függvényeket a belefoglaló hatókörből. Ezek azonban egy kifejezésbennameof()engedélyezettek.
13.7 Kifejezési utasítások
Egy expression_statement kiértékel egy adott kifejezést. A kifejezés által kiszámított érték (ha van ilyen) elvetve lesz.
expression_statement
: statement_expression ';'
;
statement_expression
: null_conditional_invocation_expression
| invocation_expression
| object_creation_expression
| assignment
| post_increment_expression
| post_decrement_expression
| pre_increment_expression
| pre_decrement_expression
| await_expression
;
Nem minden kifejezés megengedett állításként.
Megjegyzés: Az olyan kifejezések, mint
x + ypéldául ésx == 1, amelyek csak egy értéket számítanak ki (amelyeket elvetnek), nem engedélyezettek utasításként. végjegyzet
Egy expression_statement végrehajtása kiértékeli a tartalmazott kifejezést, majd átviszi a vezérlést a expression_statement végpontjára. A expression_statement végpontja akkor érhető el, ha az expression_statement elérhető.
13.8 Kiválasztási utasítások
13.8.1 Általános
A kijelölési utasítások néhány kifejezés értéke alapján kiválasztják a végrehajtáshoz szükséges lehetséges utasítások egyikét.
selection_statement
: if_statement
| switch_statement
;
13.8.2 Az if utasítás
Az if utasítás egy logikai kifejezés értéke alapján kiválaszt egy végrehajtási utasítást.
if_statement
: 'if' '(' boolean_expression ')' embedded_statement
| 'if' '(' boolean_expression ')' embedded_statement
'else' embedded_statement
;
Egy else rész a szintaxis által megengedett lexikálisan legközelebbi előzőhöz van társítva if .
Példa: Így az állítás egy
ifformábanif (x) if (y) F(); else G();egyenértékű a
if (x) { if (y) { F(); } else { G(); } }záró példa
Az if utasítás végrehajtása az alábbiak szerint történik:
- A boolean_expression (12.26.§) kiértékelése történik.
- Ha a logikai kifejezés hozamot ad
true, a vezérlőelem átkerül az első beágyazott utasításba. Ha a vezérlőelem eléri az utasítás végpontját, a vezérlőelem átkerül azifutasítás végpontjára. - Ha a logikai kifejezés hozamot ad
false, és egyelserész jelen van, a vezérlő átkerül a második beágyazott utasításba. Ha a vezérlőelem eléri az utasítás végpontját, a vezérlőelem átkerül azifutasítás végpontjára. - Ha a logikai kifejezés hozamot ad
false, és ha egyelserész nincs jelen, a vezérlőelem átkerül azifutasítás végpontjára.
Az utasítás első beágyazott utasítása if akkor érhető el, ha az if utasítás elérhető, és a logikai kifejezés nem rendelkezik állandó értékkel false.
Ha egy utasítás második beágyazott utasítása if jelen van, akkor akkor érhető el, ha az if utasítás elérhető, és a logikai kifejezés nem rendelkezik állandó értékkel true.
Az utasítás végpontja if akkor érhető el, ha legalább az egyik beágyazott utasítás végpontja elérhető. Ezenkívül egy rész nélküli utasítás végpontja if akkor érhető el, ha az else utasítás elérhető, és a logikai kifejezés nem rendelkezik állandó értékkel if.true
13.8.3 A kapcsoló utasítása
Az switch utasítás a végrehajtáshoz kiválaszt egy utasításlistát, amely a kapcsoló selector_expression értékének megfelelő kapcsolócímkével rendelkezik.
switch_statement
: 'switch' selector_expression switch_block
;
selector_expression
: '(' expression ')'
| tuple_expression
;
switch_block
: '{' switch_section* '}'
;
switch_section
: switch_label+ statement_list
;
switch_label
: 'case' pattern case_guard? ':'
| 'default' ':'
;
case_guard
: 'when' null_coalescing_expression
;
A switch_statement a kulcsszóból switcháll, amelyet egy tuple_expression vagy zárójeles kifejezés követ (amelyek mindegyike az selector_expression), majd egy switch_block. A switch_block kapcsos zárójelek közé zárt nullából vagy több switch_sectionáll. Minden switch_section egy vagy több switch_labeltartalmaz, amelyet egy statement_list követ (13.3.2. §). Minden switch_label tartalmaz case egy társított mintát (11. §), amely alapján a kapcsoló selector_expression értéke tesztelve van. Ha case_guard jelen van, kifejezésének implicit módon átalakíthatónak kell lennie a típusra bool , és a kifejezés az eset kielégítettnek tekinthető további feltételeként lesz kiértékelve.
Megjegyzés: A kényelem érdekében a switch_statement zárójelei elhagyhatók, ha a selector_expressiontuple_expression. Megírható például
switch ((a, b)) …a következőkéntswitch (a, b) …: . végjegyzet
Az utasítás szabályozási típusátswitch a kapcsoló selector_expression határozza meg.
- Ha a kapcsoló selector_expression
sbytetípusa , ,byte,intushortcharulonguintshortboolstringlongvagy egy enum_type, vagy ha az egyik ilyen típusnak megfelelő null értékű érték, akkor ez azswitchutasítás szabályozó típusa. - Ellenkező esetben, ha pontosan egy felhasználó által definiált implicit konverzió létezik a kapcsoló selector_expression típusából a következő lehetséges szabályozási típusok egyikére:
sbyte, ,byte,longstringushortshortuintulongintcharegy ilyen típusnak megfelelő null értékű értéktípus, akkor a konvertált típus azswitchutasítás szabályozási típusa. - Ellenkező esetben az utasítás szabályozási típusa
switcha kapcsoló selector_expression típusa. Hiba, ha nincs ilyen típus.
Egy default utasításban legfeljebb egy switch címke lehet.
Hiba, ha egy kapcsolócímke mintája nem alkalmazható (11.2.1. §) a bemeneti kifejezés típusára.
Hiba, ha a kapcsolócímkék mintáját a (11.3. §) a kapcsolóutasítás korábbi kapcsolócímkéinek olyan mintáinak halmaza adja meg, amelyek nem rendelkeznek esetőrrel, vagy amelyek esetőre egy igaz értékkel rendelkező állandó kifejezés.
Példa:
switch (shape) { case var x: break; case var _: // error: pattern subsumed, as previous case always matches break; default: break; // warning: unreachable, all possible values already handled. }záró példa
Az switch utasítás végrehajtása az alábbiak szerint történik:
- A kapcsoló selector_expression a rendszer kiértékeli és átalakítja a szabályozási típusra.
- A vezérlő a konvertált kapcsoló selector_expression értékének megfelelően kerül átadásra:
- A lexikálisan első minta a címkék halmazában
caseugyanabbanswitchaz utasításban, amely megegyezik a kapcsoló selector_expression értékével, és amelynek az őrkifejezése hiányzik vagy igaz értékre értékel, a vezérlő a megfeleltetett címkétcasekövető utasításlistára kerül. - Ellenkező esetben, ha egy
defaultcímke jelen van, a vezérlő a címkét követődefaultutasításlistára kerül. - Ellenkező esetben a vezérlőelem átkerül az utasítás végpontjára
switch.
- A lexikálisan első minta a címkék halmazában
Megjegyzés: A minták futásidőben való egyeztetésének sorrendje nincs meghatározva. A fordítók számára engedélyezett (de nem kötelező) a minták sorrenden kívüli egyeztetése, valamint a már egyeztetett minták eredményeinek újrafelhasználása más minták egyeztetésének eredményének kiszámításához. Ennek ellenére egy fordítóra van szükség a kifejezésnek megfelelő lexikálisan első minta meghatározásához, amelynél az ellenőrző kitétel vagy hiányzik, vagy
true-ra értékelődik ki. végjegyzet
Ha egy kapcsolószakasz utasításlistájának végpontja elérhető, fordítási időhiba lép fel. Ez az úgynevezett "nincs átesés" szabály.
Példa: A példa
switch (i) { case 0: CaseZero(); break; case 1: CaseOne(); break; default: CaseOthers(); break; }érvényes, mert egyetlen kapcsolószakasznak sincs elérhető végpontja. A C és a C++-sal ellentétben a switch szakaszok végrehajtása nem „esik át” a következő switch szakaszra, és a példa bemutatja ezt.
switch (i) { case 0: CaseZero(); case 1: CaseZeroOrOne(); default: CaseAny(); }fordítási időbeli hibát eredményez. Ha egy kapcsolószakasz végrehajtását egy másik kapcsolószakasz végrehajtása követi, explicit
goto casevagygoto defaultutasítást kell használni:switch (i) { case 0: CaseZero(); goto case 1; case 1: CaseZeroOrOne(); goto default; default: CaseAny(); break; }záró példa
Egy switch_section több címke is engedélyezett.
Példa: A példa
switch (i) { case 0: CaseZero(); break; case 1: CaseOne(); break; case 2: default: CaseTwo(); break; }érvényes. A példa nem sérti az "át nem esés" szabályt, mert a címkék
case 2:ésdefault:ugyanahhoz a switch_section-hez tartoznak.záró példa
Megjegyzés: A "nincs átesés" szabály megakadályozza a C és C++ rendszerekben előforduló hibák gyakori osztályát, amikor
breakaz utasítások véletlenül kimaradnak. A fenti utasítás szakaszaiswitchpéldául az utasítás viselkedésének befolyásolása nélkül megfordíthatók:switch (i) { default: CaseAny(); break; case 1: CaseZeroOrOne(); goto default; case 0: CaseZero(); goto case 1; }végjegyzet
Megjegyzés: A kapcsolószakaszok utasításlistája általában egy
break,goto casevagygoto defaultutasításban végződik, de az utasításlista végpontját elérhetetlenné tevő szerkezetek engedélyezettek. A logikai kifejezéswhileáltal szabályozott utasítás példáultrueismert, hogy soha nem éri el a végpontját. Hasonlóképpen, egythrowvagyreturnutasítás mindig máshová továbbítja az irányítást, és soha nem éri el a saját végpontját. Így a következő példa érvényes:switch (i) { case 0: while (true) { F(); } case 1: throw new ArgumentException(); case 2: return; }végjegyzet
Példa: Az utasítás szabályozási típusa
switchlehet a típusstring. Példa:void DoCommand(string command) { switch (command.ToLower()) { case "run": DoRun(); break; case "save": DoSave(); break; case "quit": DoQuit(); break; default: InvalidCommand(command); break; } }záró példa
Megjegyzés: A sztringegyenlőség operátoraihoz (12.14.8.§) hasonlóan az utasítás megkülönbözteti a
switchkis- és nagybetűket, és csak akkor hajt végre egy adott kapcsolószakaszt, ha a kapcsoló selector_expression sztringje pontosan megfelel acasecímkeállandónak. végjegyzet
Ha egy switch utasítás string szabályozási típusa vagy null értékű, az érték null címkeállandóként case engedélyezett.
A statement_list-ek egy switch_block-ban deklarációkat is tartalmazhatnak (§13.6). A kapcsolóblokkban deklarált helyi változó vagy állandó hatóköre a kapcsolóblokk.
A kapcsolócímke akkor érhető el, ha az alábbiak közül legalább az egyik igaz:
- A kapcsoló selector_expression állandó érték, és
- a címke egy
caseolyan címke, amelynek mintája megfelelne az adott értékkel (§11.2.1), és a címke védője hiányzik, vagy nem egy állandó kifejezés a hamis értékkel; vagy - ez egy
defaultcímke, és egyetlen kapcsolószakasz sem tartalmaz olyan esetcímkét, amelynek mintája megfelelne az értéknek, és amelynek az őre hiányzik, vagy egy állandó kifejezés igaz értékkel.
- a címke egy
- A kapcsoló selector_expression nem állandó érték, és
- A címke védőfeltétel nélküli, vagy olyan védőfeltétellel rendelkezik, amelynek értéke nem konstans hamis; vagy
- ez egy
defaultcímke, és- a switch utasítás olyan esetei között megjelenő minták összessége, amelyek nem rendelkeznek védőkkel, vagy csak olyan védőkkel rendelkeznek, amelyek értéke az állandó igaz, nem kimerítő (11.4. §) a kapcsoló irányító típusára nézve; vagy
- a kapcsoló vezérlési típusa null értékű típus, és a kapcsoló utasítások azon eseteiben megjelenő minták, amelyeknél nincsenek védők, vagy amelyek védőinek értéke az állandó igaz, nem tartalmaznak olyan mintát, amely megfelelne az értéknek
null.
- A kapcsoló címkére egy elérhető
goto casevagygoto defaultutasítás hivatkozik.
Egy adott kapcsolószakasz utasításlistája akkor érhető el, ha az switch utasítás elérhető, és a kapcsolószakasz egy elérhető kapcsolócímkét tartalmaz.
Az utasítás végpontja switch akkor érhető el, ha a kapcsolóutasítás elérhető, és az alábbiak közül legalább az egyik igaz:
- Az
switchutasítás egy elérhetőbreakutasítást tartalmaz, amely kilép aswitchutasításból. - Nincs
defaultcímke, és egyik sem- A kapcsoló selector_expression nem állandó érték, és az olyan kapcsolóutasítások esetében megjelenő minták, amelyek nem rendelkeznek védőkkel vagy olyan őrökkel, amelyek értéke az állandó igaz, nem teljes (11.4. §) a kapcsoló szabályozási típusára vonatkozóan.
- A kapcsoló selector_expression null értékű nem állandó érték, és nem jelenik meg minta az olyan kapcsolóutasítások esetei között, amelyek nem rendelkeznek őrökkel vagy olyan őrökkel, amelyeknek az értéke az állandó igaz értéke, megegyezne az értékkel
null. - A kapcsoló selector_expression állandó érték, és nincs
caseolyan címke, amelynek nincs védője, vagy amelynek védője az állandó igaz, megegyezne ezzel az értékkel.
Példa: Az alábbi kód a záradék tömör használatát
whenmutatja be:static object CreateShape(string shapeDescription) { switch (shapeDescription) { case "circle": return new Circle(2); … case var o when string.IsNullOrWhiteSpace(o): return null; default: return "invalid shape description"; } }A var-eset egyezik
nullkarakterlánccal, az üres karakterlánccal vagy bármely olyan karakterlánccal, amely kizárólag szóközöket tartalmaz. záró példa
13.9 Iterációs utasítások
13.9.1 Általános
Az iterációs utasítások ismételten végrehajtanak egy beágyazott utasítást.
iteration_statement
: while_statement
| do_statement
| for_statement
| foreach_statement
;
13.9.2 Az "while" utasítás
Az while utasítás feltételesen nulla vagy több alkalommal hajt végre beágyazott utasítást.
while_statement
: 'while' '(' boolean_expression ')' embedded_statement
;
Az while utasítás végrehajtása az alábbiak szerint történik:
- A boolean_expression (12.26.§) kiértékelése történik.
- Ha a logikai kifejezés hozamot ad
true, a vezérlőelem átkerül a beágyazott utasításba. Amikor és ha a vezérlőelem eléri a beágyazott utasítás végpontját (esetleg egycontinueutasítás végrehajtásából), a vezérlő átkerül azwhileutasítás elejére. - Ha a logikai kifejezés hozamot eredményez
false, a vezérlőelem átkerül azwhileutasítás végpontjára.
Az utasítás beágyazott utasításán while belül egy break utasítás (13.10.2. §) használható az utasítás végpontjára while történő átvitelre (így a beágyazott utasítás iterációjának befejezésére), és egy continue utasítás (13.10.3. §) használatával a vezérlőt a beágyazott utasítás végpontjára lehet továbbítani (így az utasítás másik iterációját while hajthatja végre).
Az while utasítás beágyazott utasítása akkor érhető el, ha az while utasítás elérhető, és a logikai kifejezés értéke nem konstans false.
Az utasítás végpontja while akkor érhető el, ha az alábbiak közül legalább az egyik igaz:
- Az
whileutasítás egy elérhetőbreakutasítást tartalmaz, amely kilép awhileutasításból. - Az
whileutasítás elérhető, és a logikai kifejezés nem rendelkezik állandó értékkeltrue.
13.9.3 A do utasítás
Az do utasítás feltételesen végrehajt egy beágyazott utasítást egy vagy több alkalommal.
do_statement
: 'do' embedded_statement 'while' '(' boolean_expression ')' ';'
;
Az do utasítás végrehajtása az alábbiak szerint történik:
- Az irányítás átszáll a beágyazott utasításba.
- Ha a vezérlőelem eléri a beágyazott utasítás végpontját (esetleg egy
continueutasítás végrehajtásából), a boolean_expression (12.26. §) kiértékeli a rendszer. Ha a logikai kifejezés hozamot adtrue, a vezérlőelem átkerül azdoutasítás elejére. Ellenkező esetben a vezérlőelem átkerül az utasítás végpontjárado.
Az utasítás beágyazott utasításán do belül egy break utasítás (13.10.2. §) használható az utasítás végpontjára do történő átvitelre (így a beágyazott utasítás iterációjának befejezésére), és egy continue utasítás (13.10.3. §) használatával a vezérlőt a beágyazott utasítás végpontjára lehet továbbítani (így az utasítás másik iterációját do hajthatja végre).
Az utasítás beágyazott utasítása do akkor érhető el, ha az do utasítás elérhető.
Az utasítás végpontja do akkor érhető el, ha az alábbiak közül legalább az egyik igaz:
- Az
doutasítás egy elérhetőbreakutasítást tartalmaz, amely kilép adoutasításból. - A beágyazott utasítás végpontja elérhető, és a logikai kifejezés nem rendelkezik állandó értékkel
true.
13.9.4 A nyilatkozat
Az for utasítás kiértékeli az inicializálási kifejezések sorozatát, majd amíg egy feltétel igaz, ismételten végrehajt egy beágyazott utasítást, és kiértékeli az iterációs kifejezések sorozatát.
for_statement
: 'for' '(' for_initializer? ';' for_condition? ';' for_iterator? ')'
embedded_statement
;
for_initializer
: local_variable_declaration
| statement_expression_list
;
for_condition
: boolean_expression
;
for_iterator
: statement_expression_list
;
statement_expression_list
: statement_expression (',' statement_expression)*
;
A for_initializer, ha jelen van, egy helyi_változó_deklarációból (§13.6.2) vagy vesszőkkel elválasztott kifejezés_kifejtések (§13.7) listájából áll. Egy for_initializer által deklarált helyi változó hatóköre a for_initializer, for_condition, for_iterator és embedded_statement.
A for_condition , ha van ilyen, boolean_expression kell lennie (12.26. §).
A for_iterator, ha van, egy vesszőkkel elválasztott statement_expression (§13.7) listából áll.
Az for utasítás végrehajtása az alábbiak szerint történik:
- Ha for_initializer van jelen, a változó inicializálói vagy utasításkifejezései a megírásuk sorrendjében lesznek végrehajtva. Ez a lépés csak egyszer lesz végrehajtva.
- Ha egy for_condition van jelen, a kiértékelése történik.
- Ha a for_condition nincs jelen, vagy ha a kiértékelési hozamok
true, a rendszer átviszi az ellenőrzést a beágyazott utasításba. Amikor és ha a vezérlőelem eléri a beágyazott utasítás végpontját (esetleg egycontinueutasítás végrehajtásától), a for_iterator kifejezéseit egymás után kiértékeli a rendszer, majd egy másik iterációt hajt végre a fenti lépésben szereplő for_condition kiértékelésétől kezdve. - Ha a for_condition jelen van, és a kiértékelési hozamok
false, az ellenőrzés átkerül azforutasítás végpontjára.
A for utasításon belül, annak beágyazott utasításában egy break utasítás (§13.10.2) használható a vezérlés for utasítás végpontjára történő átvitelére (így befejezve a beágyazott utasítás iterálását). Továbbá, egy continue utasítás (§13.10.3) alkalmazható a vezérlés átadására a beágyazott utasítás végpontjához (így végrehajtva a for_iterator és elindítva az for utasítás újabb iterációját, kezdve a for_condition).
A for utasítás beágyazott utasítása elérhető, ha az alábbi feltételek egyike igaz:
- Az
forkijelentés elérhető, és nincs for_condition. - Az
forutasítás elérhető, és egy for_condition van jelen, és nem rendelkezik állandó értékkelfalse.
Az utasítás végpontja for akkor érhető el, ha az alábbiak közül legalább az egyik igaz:
- Az
forutasítás egy elérhetőbreakutasítást tartalmaz, amely kilép aforutasításból. - Az
forutasítás elérhető, és egy for_condition van jelen, és nem rendelkezik állandó értékkeltrue.
13.9.5 A foreach utasítás
13.9.5.1 Általános
Az foreach utasítás felsorolja a gyűjtemény elemeit, és beágyazott utasítást hajt végre a gyűjtemény minden eleméhez.
foreach_statement
: 'await'? 'foreach' '(' ref_kind? local_variable_type identifier
'in' expression ')' embedded_statement
;
A foreach utasítás local_variable_type és azonosítója deklarálja az utasítás iterációs változóját . Ha az var azonosítót a local_variable_type adja meg, és nincs megnevezve var típus a hatókörben, az iterációs változó implicit módon beírt iterációs változónak minősül, és a típus az utasítás elemtípusa foreach lesz az alábbiak szerint.
Ez fordítási időbeli hiba, ha mind await, mind ref_kind jelen van egy foreach statement-ben.
Ha a foreach_statement mindkettőt vagy egyiket sem refreadonlytartalmazza, az iterációs változó egy írásvédettként kezelt változót jelöl. Ellenkező esetben, ha foreach_statementref tartalmaz readonly nélkül, az iterációs változó írható változót jelöl.
Az iterációs változó egy helyi változónak felel meg, amelynek hatóköre kiterjed a beágyazott utasításra. Az foreach utasítás végrehajtása során az iterációs változó azt a gyűjtési elemet jelöli, amelyhez jelenleg iterációt végeznek. Ha az iterációs változó írásvédett változót jelöl, fordítási időhiba lép fel, ha a beágyazott utasítás megkísérli módosítani (hozzárendeléssel vagy ++-- operátorokkal), vagy hivatkozási vagy kimeneti paraméterként adja át.
Az utasítás fordítási foreach idejének feldolgozása először a kifejezés gyűjteménytípusát (C), enumerátortípusát (E) és iterációs típusát (Tref Tvagy ref readonly T) határozza meg.
A meghatározás hasonló a szinkron és az aszinkron verziókhoz. A különböző metódusokkal és visszatérési típusokkal rendelkező különböző felületek megkülönböztetik a szinkron és az aszinkron verziókat. Az általános folyamat az alábbiak szerint halad. A "«" és a "»" nevek a szinkron és aszinkron iterátorok tényleges nevének helyőrzői. A «GetEnumerator», «MoveNext», «IEnumerable»<T>, «IEnumerator»<T> és egyéb megkülönböztetések megengedett típusait a 13.9.5.2. §-ban, szinkron utasítás esetén foreach a 13.9.5.3foreach.
- Határozza meg, hogy a
Xtípusa rendelkezik-e a megfelelő "GetEnumerator" metódussal:- Tagkeresést végezhet a típuson
Xa "GetEnumerator" azonosítóval és típusargumentumok nélkül. Ha a tagkeresés nem hoz létre egyezést, vagy kétértelműséget eredményez, vagy olyan egyezést hoz létre, amely nem metóduscsoport, ellenőrizze, hogy a 2. lépésben leírtak szerint enumerálható felület van-e. Javasoljuk, hogy figyelmeztetést adunk ki, ha a tagkeresés nem csak metóduscsoportot ad vissza, vagy nincs egyezés. - Túlterhelés feloldása az eredményként kapott metóduscsoport és egy üres argumentumlista használatával. Ha a túlterhelés feloldása nem eredményez alkalmazható metódusokat, kétértelműséget eredményez, vagy egyetlen legjobb módszert eredményez, de ez a módszer statikus vagy nem nyilvános, ellenőrizze az alábbiakban ismertetett számbavételi felületet. Javasoljuk, hogy figyelmeztetést adjon ki, ha a túlterhelés feloldása nem hoz létre semmit, kivéve egy egyértelmű nyilvános példány metódusát, vagy nem alkalmazható metódusokat.
- Ha a GetEnumerator metódus visszatérési típusa
Enem osztály, struktúra vagy felülettípus, hibát jelez, és nem hajt végre további lépéseket. - Tagkeresés
Evégrehajtása azonosítóvalCurrentés típusargumentumok nélkül. Ha a tagkeresés nem eredményez egyezést, az eredmény hiba, vagy az eredmény nem más, mint egy nyilvános példánytulajdonság, amely lehetővé teszi az olvasást, hibát eredményez, és nem hajt végre további lépéseket. - Tagkeresést
Evégezhet a "MoveNext" azonosítóval és típusargumentumok nélkül. Ha a tagkeresés nem hoz létre egyezést, az eredmény hiba, vagy az eredmény egy metóduscsoporton kívül bármi, hibát eredményez, és nem hajt végre további lépéseket. - Túlterhelésfeloldás végrehajtása a metóduscsoporton üres argumentumlistával. Ha a túlterhelés feloldása a következőt eredményezi: nincs alkalmazható módszer; kétértelműség; vagy egyetlen legjobb módszer, de ez a módszer statikus vagy nem nyilvános, vagy visszatérési típusa nem engedélyezett visszatérési típus; hibát eredményez, és nem hajt végre további lépéseket.
- A gyűjtemény típusa
X, az enumerátor típusaE, az iteráció típusa pedig aCurrenttulajdonság típusa.
- Tagkeresést végezhet a típuson
- Ellenkező esetben ellenőrizze, hogy van-e felsorolható felület:
- Ha az összes típus
Tᵢközött, amelynek implicit konverziója "IEnumerable»XTi<"-ra > történik, van egy egyedi típusT, amelyTnemdynamic, és az összes többiTᵢesetében implicit átalakítás történik «IEnumerable»<T-ről> «IEnumerable»<Ti-ra>, akkor a gyűjtemény típusa az interfész «IEnumerable»<T>, az enumerátor típusa az «IEnumerator»<T> interfész, és az iteráció típusa .T - Ellenkező esetben, ha egynél több ilyen típus
Tis létezik, akkor hiba lép fel, és ne tegyen további lépéseket.
- Ha az összes típus
Megjegyzés: Ha a kifejezés értéke
nullvan, a rendszer futásidőben ad ki egySystem.NullReferenceExceptionértéket. végjegyzet
A végrehajtás egy adott foreach_statement eltérő módon valósítható meg; például teljesítménybeli okokból, amennyiben a viselkedés összhangban van a 13.9.5.2.
13.9.5.2 Szinkron foreach
A szinkron foreach nem tartalmazza a await kulcsszót a foreach kulcsszó előtt. A gyűjteménytípus, az enumerálási típus és az iterációs típus meghatározása a 13.9.5.1.
- A "GetEnumerator" egy
GetEnumeratormetódus. - A "MoveNext" egy
MoveNextvisszatérésibooltípusú metódus. - «IEnumerable»<T> a
System.Collections.Generic.IEnumerable<T>felület. - «IEnumerator»<T> a
System.Collections.Generic.IEnumerator<T>felület.
Ezenkívül a 13.9.5.1.
- Ha a
Xtípusa tömbtípus, akkor implicit hivatkozási átalakítás történik arra aXfelületreIEnumerable<T>, aholTa tömbXelemtípusa (17.2.3. §). - Ha a
Xtípusa , akkor van egy implicit átalakítás adynamica interfészre (IEnumerable). A gyűjtemény típusa azIEnumerableinterfész, az enumerátor típusa pedig azIEnumeratorinterfész. Ha avarazonosítót local_variable_typeként adjuk meg, akkor az iteráció típusadynamic; ellenkező esetben a(z)object.
Ha a 13.9.5.1. § -ban szereplő folyamat egyetlen gyűjteménytípus, enumerátortípus és iterációs típus létrehozása nélkül fejeződik be, a következő lépéseket kell végrehajtania:
- Ha implicit átalakítás történik a felületről a felületre
X, akkor a gyűjtemény típusa ez az interfész, az enumerátor típusa az interfészSystem.Collections.IEnumerable, az iteráció típusa pedig azSystem.Collections.IEnumerator.object - Ellenkező esetben hibaüzenet jelenik meg, és nem történik további lépés.
Az foreach űrlap nyilatkozata
foreach (V v in x) «embedded_statement»
akkor a következőnek felel meg:
{
E e = ((C)(x)).GetEnumerator();
try
{
while (e.MoveNext())
{
V v = (V)(T)e.Current;
«embedded_statement»
}
}
finally
{
... // Dispose e
}
}
A változó e nem látható a kifejezés x , a beágyazott utasítás vagy a program bármely más forráskódja számára, illetve nem érhető el. A változó v csak olvasható a beágyazott utasításban. Ha nincs explicit átalakítás (§10.3) az T (az iterációs típus) és a V (a local_variable_type a foreach utasításban) között, hibaüzenet jelenik meg, és nem történik további lépés.
Ha az iterációs változó referenciaváltozó (§9.7), egy utasítás a következő formában
foreach (ref V v in x) «embedded_statement»
akkor a következőnek felel meg:
{
E e = ((C)(x)).GetEnumerator();
try
{
while (e.MoveNext())
{
ref V v = ref e.Current;
«embedded_statement»
}
}
finally
{
... // Dispose e
}
}
A változó e nem látható vagy nem érhető el a kifejezés x , a beágyazott utasítás vagy a program bármely más forráskódja számára. A referenciaváltozó v a beágyazott utasításban olvasható-írható, de v nem rendelhető át újra (12.23.3. §). Ha nincs azonosságkonverzió (§10.2.2) T (az iterációs típusból) V (a kijelentés foreach típusára), akkor hiba következik be, és nem történik további lépés.
Az foreach utasítás formája hasonló másik formával bír, de a referenciaváltozó foreach (ref readonly V v in x) «embedded_statement» a beágyazott utasításban van v, ezért nem lehet újra beállítani vagy hozzárendelni.
A hurokon belüli while elhelyezés v fontos ahhoz, hogy a rendszer hogyan rögzíti (12.21.6.2.2. §) a embedded_statement előforduló névtelen függvények által.
Példa:
int[] values = { 7, 9, 13 }; Action f = null; foreach (var value in values) { if (f == null) { f = () => Console.WriteLine("First value: " + value); } } f();Ha
va kibontott formában awhilecikluson kívül deklarálnák, akkor az minden iteráció között meg lesz osztva, és aforciklus utáni értéke a végső érték lenne,13amit a hívásafnyomtatna ki. Ehelyett, mivel minden iteráció saját változóvalvrendelkezik, az első iterációban rögzítettférték továbbra is az értéket7fogja tárolni, ami a nyomtatni fog. (Vegye figyelembe, hogy a C# korábbi verziói avcikluson kívül deklaráltakwhile.)záró példa
A blokk törzse a finally következő lépések szerint épül fel:
Ha implicit átalakítás történik a felületről
EaSystem.IDisposablefelületre, akkorHa
Enem null értékű, akkor afinallyzáradék a következő szemantikai megfelelője lesz:finally { ((System.IDisposable)e).Dispose(); }Ellenkező esetben a
finallyzáradék ki van bontva a szemantikai megfelelője:finally { System.IDisposable d = e as System.IDisposable; if (d != null) { d.Dispose(); } }kivéve, hogy ha
Eegy értéktípus vagy egy értéktípusra példányosított típusparaméter, akkorekonvertálásaSystem.IDisposable-vé nem okozhat boxolást.
Ellenkező esetben, ha
Elezárt típus, afinallyzáradék üres blokkra lesz kibontva:finally {}Ellenkező esetben a záradék a következőre lesz kibontva
finally:finally { System.IDisposable d = e as System.IDisposable; if (d != null) { d.Dispose(); } }
A helyi változó d nem látható és nem érhető el semmilyen felhasználói kód számára. Különösen nem ütközik más olyan változókkal, amelyek hatóköre tartalmazza a blokkot finally .
A tömb elemeinek bejárási sorrendje foreach a következő: Az egydimenziós tömbök esetében az elemek növekvő indexrendben haladnak át, kezdve a 0 indexkel, és indexgel Length – 1végződnek. Többdimenziós tömbök esetén az elemek úgy haladnak be, hogy a jobb szélső dimenzió indexei előbb növekednek, majd a következő bal dimenzió, és így tovább balra.
Példa: Az alábbi példa egy kétdimenziós tömb minden értékét elemsorrendben nyomtatja ki:
class Test { static void Main() { double[,] values = { {1.2, 2.3, 3.4, 4.5}, {5.6, 6.7, 7.8, 8.9} }; foreach (double elementValue in values) { Console.Write($"{elementValue} "); } Console.WriteLine(); } }A létrehozott kimenet a következő:
1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9záró példa
Példa: Az alábbi példában
int[] numbers = { 1, 3, 5, 7, 9 }; foreach (var n in numbers) { Console.WriteLine(n); }
ntípusáról kikövetkeztették, hogyint, aminumbersiterációtípusa.záró példa
13.9.5.3 kivár foreach
Az aszinkron foreach a szintaxist await foreach használja. A gyűjteménytípus, az enumerálási típus és az iterációs típus meghatározása a 13.9.5.1.
- A "GetEnumerator" egy
GetEnumeratorAsyncolyan metódus, amely várandós visszatérési típussal rendelkezik (12.9.9.2. §). - A "MoveNext" egy
MoveNextAsyncolyan metódus, amely várandós visszatérési típussal rendelkezik (12.9.9.2. §), ahol a await_expression besorolásabool(12.9.9.3. §). - «IEnumerable»<T> a
System.Collections.Generic.IAsyncEnumerable<T>felület. - «IEnumerator»<T> a
System.Collections.Generic.IAsyncEnumerator<T>felület.
Hiba, ha az utasítás iterációs típusaawait foreach referenciaváltozó (9.7. §).
Az await foreach űrlap nyilatkozata
await foreach (T item in enumerable) «embedded_statement»
szemantikailag egyenértékű a következő értékeket:
var enumerator = enumerable.GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync())
{
T item = enumerator.Current;
«embedded_statement»
}
}
finally
{
// dispose of enumerator as described later in this clause.
}
Abban az esetben, ha a kifejezés enumerable metódushívási kifejezést jelöl, és az egyik paramétert a rendszer a EnumeratorCancellationAttribute (23.5.8. §) jellel jelöli, a CancellationToken rendszer átadja a GetAsyncEnumerator metódusnak. Más kódtár-metódusokhoz szükség lehet a CancellationToken továbbításra GetAsyncEnumerator. Ha ezek a módszerek a kifejezés enumerablerészét képezik, a jogkivonatokat egyetlen jogkivonatba kell egyesíteni, mintha CreateLinkedTokenSource a kifejezés és annak tulajdonsága Token lenne.
A blokk törzse a finally következő lépések szerint épül fel:
Ha
Eolyan akadálymentesDisposeAsync()metódussal rendelkezik, ahol a visszatérési típus várható (12.9.9.2. §), afinallyzáradék ki van bontva a következő szemantikai megfelelőre:finally { await e.DisposeAsync(); }Ellenkező esetben, ha implicit átalakítás történik az illesztőről
EaSystem.IAsyncDisposablefelületre, ésEnem null értékű, akkor afinallyzáradék ki lesz bontva a következő szemantikai megfelelőjeként:finally { await ((System.IAsyncDisposable)e).DisposeAsync(); }kivéve, hogy ha
Eegy értéktípus vagy egy értéktípusra példányosított típusparaméter, akkorekonvertálásaSystem.IAsyncDisposable-vé nem okozhat boxolást.Ellenkező esetben, ha
Eegyref structtípus, és elérhetőDispose()metódussal rendelkezik, afinallyzáradék ki van bontva a következő szemantikai megfelelőjeként:finally { e.Dispose(); }Ellenkező esetben, ha
Elezárt típus, afinallyzáradék üres blokkra lesz kibontva:finally {}Ellenkező esetben a záradék a következőre lesz kibontva
finally:finally { System.IAsyncDisposable d = e as System.IAsyncDisposable; if (d != null) { await d.DisposeAsync(); } }
A helyi változó d nem látható és nem érhető el semmilyen felhasználói kód számára. Különösen nem ütközik más olyan változókkal, amelyek hatóköre tartalmazza a blokkot finally .
Megjegyzés:
await foreachNem szükséges szinkron módon megsemmisítenieazokat, ha nem áll rendelkezésre aszinkron megsemmisítési mechanizmus. végjegyzet
13.10 Ugró utasítások
13.10.1 Általános
A jump utasítások feltétel nélkül átviszik az irányítást.
jump_statement
: break_statement
| continue_statement
| goto_statement
| return_statement
| throw_statement
;
Az a hely, ahová a jump utasítás átviszi a vezérlőt, a jump utasítás céljának nevezzük.
Ha egy ugrási utasítás egy blokkon belül történik, és a ugróutasítás célja kívül esik a blokkon, a jump utasítás azt mondja, hogy lépjen ki a blokkból . Bár a jump utasítás átviheti a vezérlőt egy blokkból, a vezérlő soha nem helyezhető át blokkba.
Az ugrási utasítások végrehajtását bonyolítja a beavatkozó try utasítások jelenléte. Ilyen try utasítások hiányában a jump utasítás feltétel nélkül átviszi az irányítást a jump utasításból a célba. Ilyen beavatkozó try utasítások jelenlétében a végrehajtás összetettebb. Ha a jump utasítás kilép egy vagy több try blokkból a társított finally blokkokkal, a vezérlő kezdetben átkerül a finally legbelső try utasítás blokkjába. Amikor és ha a vezérlőelem eléri a finally blokk végpontját, a vezérlő átkerül a finally következő záró try utasítás blokkjába. Ez a folyamat addig ismétlődik, amíg az finally összes beavatkozó try utasítás blokkjainak végrehajtása meg nem történik.
Példa: Az alábbi kódban
class Test { static void Main() { while (true) { try { try { Console.WriteLine("Before break"); break; } finally { Console.WriteLine("Innermost finally block"); } } finally { Console.WriteLine("Outermost finally block"); } } Console.WriteLine("After break"); } }A két
finallyutasításhoz társítotttryblokkok végrehajtásra kerülnek, mielőtt a vezérlés átadásra kerülne a jump utasítás célpontjára. A létrehozott kimenet a következő:Before break Innermost finally block Outermost finally block After breakzáró példa
13.10.2 A megszakító utasítás
Az break utasítás kilép a legközelebbi switch, while, do, for vagy foreach utasításból.
break_statement
: 'break' ';'
;
Az break utasítás célpontja a legközelebbi záró switch, while, do, for vagy foreach utasítás végpontja. Ha egy break utasítás nincs bezárva egy switch, while, do, for vagy foreach utasítással, fordítási időhiba lép fel.
Ha több switch, while, do, forvagy foreach utasítás van egymásba ágyazva, az break utasítás csak a legbelső utasításra vonatkozik. Az irányítás több beágyazási szint közötti átviteléhez egy utasítást goto (13.10.4. §) kell használni.
Az break utasítás nem tud kilépni egy finally blokkból (13.11. §). Ha egy break utasítás egy finally blokkon belül történik, az utasítás céljának break ugyanabban finally a blokkban kell lennie; ellenkező esetben fordítási időhiba lép fel.
Az break utasítás végrehajtása az alábbiak szerint történik:
- Ha az
breakutasítás kilép egy vagy többtryblokkból a társítottfinallyblokkokkal, a vezérlő kezdetben afinallylegbelsőtryutasítás blokkjába kerül. Amikor és ha a vezérlőelem eléri afinallyblokk végpontját, a vezérlő átkerül afinallykövetkező zárótryutasítás blokkjába. Ez a folyamat addig ismétlődik, amíg azfinallyösszes beavatkozótryutasítás blokkjainak végrehajtása meg nem történik. - A vezérlés átkerül az
breakutasítás célpontjára.
Mivel egy break utasítás feltétel nélkül átviszi az irányítást máshová, az utasítás végpontja break soha nem érhető el.
13.10.3 A folytatási utasítás
A continue utasítás elindítja a legközelebbi while, do, for vagy foreach utasítás új iterációját.
continue_statement
: 'continue' ';'
;
continue utasítás célja a legközelebbi while, do, for vagy foreach utasítás beágyazott utasításának végpontja. Ha egy continue utasítást nem zár be while, do, for vagy foreach utasítás, fordítási időhiba lép fel.
Ha több while, do, forvagy foreach utasítás van egymásba ágyazva, az continue utasítás csak a legbelső utasításra vonatkozik. Az irányítás több beágyazási szint közötti átviteléhez egy utasítást goto (13.10.4. §) kell használni.
Az continue utasítás nem tud kilépni egy finally blokkból (13.11. §). Ha egy continue utasítás egy finally blokkon belül történik, az utasítás céljának continue ugyanabban finally a blokkban kell lennie; ellenkező esetben fordítási időhiba lép fel.
Az continue utasítás végrehajtása az alábbiak szerint történik:
- Ha az
continueutasítás kilép egy vagy többtryblokkból a társítottfinallyblokkokkal, a vezérlő kezdetben afinallylegbelsőtryutasítás blokkjába kerül. Amikor és ha a vezérlőelem eléri afinallyblokk végpontját, a vezérlő átkerül afinallykövetkező zárótryutasítás blokkjába. Ez a folyamat addig ismétlődik, amíg azfinallyösszes beavatkozótryutasítás blokkjainak végrehajtása meg nem történik. - A vezérlés átkerül az
continueutasítás célpontjára.
Mivel egy continue utasítás feltétel nélkül átviszi az irányítást máshová, az utasítás végpontja continue soha nem érhető el.
13.10.4 A goto utasítás
Az goto utasítás egy címkével jelölt utasításra továbbítja a vezérlőt.
goto_statement
: 'goto' identifier ';'
| 'goto' 'case' constant_expression ';'
| 'goto' 'default' ';'
;
Az gotoazonosító utasítás célja az adott címkével ellátott utasítás. Ha a megadott nevű címke nem létezik az aktuális függvénytagban, vagy ha az goto utasítás nem tartozik a címke hatókörébe, fordítási időhiba lép fel.
Megjegyzés: Ez a szabály lehetővé teszi egy
gotoutasítás használatát, hogy átvihesse a vezérlést egy beágyazott hatókörből , de nem beágyazott hatókörbe . A példábanclass Test { static void Main(string[] args) { string[,] table = { {"Red", "Blue", "Green"}, {"Monday", "Wednesday", "Friday"} }; foreach (string str in args) { int row, colm; for (row = 0; row <= 1; ++row) { for (colm = 0; colm <= 2; ++colm) { if (str == table[row,colm]) { goto done; } } } Console.WriteLine($"{str} not found"); continue; done: Console.WriteLine($"Found {str} at [{row}][{colm}]"); } } }egy
gotoutasítással átviheti a vezérlést egy beágyazott hatókörből.végjegyzet
Az goto case utasítás célja a közvetlenül tartalmazó switch utasítás utasításlistája (§13.8.3), amely egy case olyan címkét tartalmaz, amelynek mintája az adott állandó értéket tükrözi, és nincs feltétel. Ha az goto case utasítás nem tartalmaz switch utasítást, ha a legközelebbi belefoglaló switch utasítás nem tartalmaz ilyent case, vagy ha a constant_expression nem implicit módon konvertálható (10.2. §) a legközelebbi belefoglaló switch utasítás szabályozási típusára, fordítási időhiba lép fel.
Az goto default utasítás célja a közvetlenül körülvevő switch utasítás utasításlistája (§13.8.3), amely egy default címkét tartalmaz. Ha az goto default utasítást nem egy switch utasítás határolja, vagy ha a legközelebbi lezáró switch utasítás nem tartalmaz default címkét, fordítási időhiba lép fel.
Az goto utasítás nem tud kilépni egy finally blokkból (13.11. §). Ha egy goto utasítás egy finally blokkon belül történik, az goto utasítás céljának ugyanabban finally a blokkban kell lennie, vagy ellenkező esetben fordítási időhiba lép fel.
Az goto utasítás végrehajtása az alábbiak szerint történik:
- Ha az
gotoutasítás kilép egy vagy többtryblokkból a társítottfinallyblokkokkal, a vezérlő kezdetben afinallylegbelsőtryutasítás blokkjába kerül. Amikor és ha a vezérlőelem eléri afinallyblokk végpontját, a vezérlő átkerül afinallykövetkező zárótryutasítás blokkjába. Ez a folyamat addig ismétlődik, amíg azfinallyösszes beavatkozótryutasítás blokkjainak végrehajtása meg nem történik. - A vezérlés átkerül az
gotoutasítás célpontjára.
Mivel egy goto utasítás feltétel nélkül átviszi az irányítást máshová, az utasítás végpontja goto soha nem érhető el.
13.10.5 A visszatérési utasítás
Az return utasítás vezérlést ad a függvénytag aktuális hívójának, amelyben a visszatérési utasítás megjelenik, és opcionálisan értéket vagy egy változó_hivatkozást ad vissza (9.5. §).
return_statement
: 'return' ';'
| 'return' expression ';'
| 'return' 'ref' variable_reference ';'
;
A kifejezés nélküli return_statement-et 'return-nincs-érték' típusnak nevezzük; a kifejezést tartalmazót 'return-referenciával' típusnak hívják; és a csak kifejezést tartalmazót 'return-értékkel' típusnak nevezzük.
Fordítási időhiba, ha érték nélküli visszatérést használ egy olyan metódusból, amely érték szerint vagy hivatkozás szerint van deklarálva (§15.6.1).
Fordítási időhiba, ha egy metódus, amely visszatérési értékként referencia szerint tér vissza, olyan metódusként van deklarálva, amely nem ad vissza értéket, vagy érték szerint ad vissza.
Fordítási időhiba lép fel, ha egy értéket visszaadó metódus visszatérési értéket ad, miközben az "értéket nem ad vissza" vagy "referenciaként ad vissza" módszerként van deklarálva.
Fordítási időbeli hiba, ha kifejezés nem variable_reference vagy olyan változóra mutató hivatkozás, amelynek ref-safe-contextje nem hívókörnyezete (§9.7.2).
Fordítási időhiba, ha a method_modifiermetódusból származó visszatérési értéket használ.
A függvénytagok azt mondják, hogy akkor számítanak ki értéket, ha az egy visszaadott érték szerinti metódus (15.6.11. §), egy tulajdonság vagy indexelő, illetve felhasználó által definiált operátor értékenkénti lekérése. Azok a függvénytagok, amelyek nem adnak vissza értéket, olyan metódusok, amelyek hatékony visszatérési típussal void rendelkeznek, továbbá ilyenek a tulajdonságok és az indexelők beállító függvényei, az események hozzáadó és eltávolító függvényei, az példánykonstruktorok, a statikus konstruktorok és a véglegesítők. A referencia szerint visszatérő függvénytagok nem számítanak ki értéket.
Visszatérési érték esetén léteznie kell egy implicit átalakításnak a kifejezés típusától az azt tartalmazó függvénytag tényleges visszatérési típusára (§10.2 és §15.6.11). Referenciaként való visszatérés esetén identitásátalakításnak (§10.2.2) kell léteznie a kifejezés típusa és az azt tartalmazó függvénymember tényleges visszatérési típusa között.
return az utasítások névtelen függvénykifejezések törzsében is használhatók (12.21.§), és részt vehetnek annak meghatározásában, hogy mely átalakítások léteznek ezekhez a függvényekhez (10.7.1. §).
Fordítási időben jelentkező hiba, ha egy return utasítás megjelenik egy finally blokkban (§13.11).
Az return utasítás végrehajtása az alábbiak szerint történik:
- A visszatérési érték esetében a kifejezés kiértékelésre kerül, és értéke implicit konverzióval átalakul a tartalmazó függvény tényleges visszatérési típusává. Az átalakítás eredménye a függvény által előállított eredményérték lesz. Visszatérés referencia által esetén a kifejezés kiértékelődik, és az eredményt változóként kell osztályozni. Ha a belefoglaló metódus visszatér referencia szerint, és tartalmaz
readonly, akkor az eredményül kapott változó írásvédett. - Ha az
returnutasítást egy vagy többtryvagycatchblokk tartalmazza, amelyek társítottfinallyblokkokkal rendelkeznek, a vezérlés kezdetben a legbelsőfinallyutasítástryblokkjába kerül. Amikor és ha a vezérlőelem eléri afinallyblokk végpontját, a vezérlő átkerül afinallykövetkező zárótryutasítás blokkjába. Ez a folyamat addig ismétlődik, amíg azfinallyösszes belefoglalótryutasítás blokkjait végre nem hajtják. - Ha a tartalmazó függvény nem aszinkron függvény, a rendszer visszaadja a vezérlőt a hívónak az eredményértékkel együtt, ha van ilyen.
- Ha a tartalmazó függvény aszinkron függvény, a rendszer visszaadja a vezérlőt az aktuális hívónak, és az eredményértéket( ha van ilyen) a visszatérési feladatban rögzíti a rendszer a (15.14.3. §) szerint.
Mivel egy return utasítás feltétel nélkül átviszi az irányítást máshová, az utasítás végpontja return soha nem érhető el.
13.10.6 A dobásról szóló nyilatkozat
Az throw utasítás kivételt jelez.
throw_statement
: 'throw' expression? ';'
;
Egy throw kifejezéssel rendelkező utasítás kivételt eredményez a kifejezés kiértékelésével. A kifejezésnek implicit módon átalakíthatónak System.Exceptionkell lennie a kifejezésre, és a kifejezés kiértékelésének eredménye át lesz alakítva System.Exception a dobás előtt. Ha az átalakítás eredménye egy nulllesz, System.NullReferenceException akkor a rendszer a következőt dobja ki.
Egy throw kifejezés nélküli utasítás csak blokkokban catch használható, ebben az esetben ez az utasítás újra elveti az adott catch blokk által jelenleg kezelt kivételt.
Mivel egy throw utasítás feltétel nélkül átviszi az irányítást máshová, az utasítás végpontja throw soha nem érhető el.
Kivétel esetén a vezérlő átkerül az első catch záradékba egy olyan záró try utasításban, amely képes kezelni a kivételt. Az a folyamat, amely a kivétel kivezetésétől a vezérlő megfelelő kivételkezelőbe való átvitelének pontjától a megfelelő kivételkezelőbe való átvitelig zajlik, kivételpropagálásnak nevezzük. A kivétel propagálása a következő lépések ismételt kiértékelését foglalja magában, amíg a catch kivételnek megfelelő záradék nem található. Ebben a leírásban a dobáspont kezdetben az a hely, ahol a kivételt eldobják. Ezt a viselkedést a (22.4. §) határozza meg.
Az aktuális függvénytagban minden
tryolyan utasítást megvizsgálunk, amely a dobáspontot tartalmazza. Minden utasításSesetében a legbelsőtryutasítástól kezdve a legkülsőtryutasításig a következő lépések lesznek kiértékelve:- Ha a
tryblokkSbefogja a dobópontot, ésSegy vagy többcatchzáradékkal rendelkezik, acatchzáradékok megjelenési sorrendben vannak megvizsgálva, hogy megtalálják a kivételhez megfelelő kezelőt. Az elsőcatchzáradék, amely egy kivételtípustThatároz meg (vagy egy olyan típusparamétert, amely futásidőben kivételtípustTjelöl), és amelybőlEa futtatási idő típusaTszármazik, egyezésnek minősül. Ha a záradék kivételszűrőt tartalmaz, a kivételobjektum hozzá lesz rendelve a kivételváltozóhoz, és kiértékeli a kivételszűrőt. Ha egycatchzáradék kivételszűrőt tartalmaz, akkor acatchzáradék egyezésnek minősül, ha a kivételszűrő kiértékeli a következőttrue: . Az általánoscatch(13.11.11.)-záradék minden kivételtípus esetében egyezésnek minősül. Ha megtalálható egy megfelelőcatchzáradék, a kivétel propagálása azzal fejeződik be, hogy a vezérlést áthelyezi a záradék blokkjára. - Ellenkező esetben, ha a
tryblokk vagy egycatchblokkSa dobópontot zárja be, és haSvan blokkjafinally, akkor a vezérlő átkerül afinallyblokkba. Ha afinallyblokk egy másik kivételt ad ki, az aktuális kivétel feldolgozása leáll. Ellenkező esetben, ha a vezérlő eléri afinallyblokk végpontját, az aktuális kivétel feldolgozása folytatódik.
- Ha a
Ha egy kivételkezelő nem található az aktuális függvényhívásban, a függvényhívás leáll, és az alábbiak egyike következik be:
Ha az aktuális függvény nem aszinkron, a fenti lépések ismétlődnek a függvény hívója számára a függvénytag meghívását tartalmazó utasításnak megfelelő dobóponttal.
Ha az aktuális függvény aszinkron és feladat-visszatérítő, a kivételt a visszatérési feladat rögzíti, amely hibás vagy megszakított állapotba kerül, ahogy azt a §15.14.3 leírja.
Ha az aktuális függvény aszinkron és
void-értéket ad vissza, az aktuális szál szinkronizálási környezetét értesítjük a 15.14.4. szakasz szerint.
Ha a kivételfeldolgozás leállítja az aktuális szál összes függvénytag-meghívását, ami azt jelzi, hogy a szál nem rendelkezik kezelőjellel a kivételhez, akkor a szál maga is leáll. Az ilyen megszüntetés hatása implementációban van meghatározva.
13.11 A próbautasítás
Az try utasítás egy mechanizmust biztosít a blokk végrehajtása során előforduló kivételek elfogására. Emellett az try utasítás lehetővé teszi egy kódblokk megadását is, amely mindig végrehajtásra kerül, amikor a vezérlő elhagyja az utasítást try .
try_statement
: 'try' block catch_clauses
| 'try' block catch_clauses? finally_clause
;
catch_clauses
: specific_catch_clause+
| specific_catch_clause* general_catch_clause
;
specific_catch_clause
: 'catch' exception_specifier exception_filter? block
| 'catch' exception_filter block
;
exception_specifier
: '(' type identifier? ')'
;
exception_filter
: 'when' '(' boolean_expression ')'
;
general_catch_clause
: 'catch' block
;
finally_clause
: 'finally' block
;
A try_statement a kulcsszóból try és egy blokk-ból áll, majd nulla vagy több catch_clauses, végül egy opcionális finally_clause következhet. Legalább egy catch_clause vagy finally_clause kell lennie.
Egy exception_specifier-ben a típus, vagy ha ez egy type_parameter, annak tényleges alaposztálya, System.Exception vagy az abból származó típus kell, hogy legyen.
Ha egy catch záradék mind a class_type és azonosítót megad, akkor az adott névvel és típussal rendelkező kivételváltozó deklarálódik. A kivételváltozó bekerül a specific_catch_clause deklarációs területébe (7.3. §). A exception_filter és catch blokk végrehajtása során a kivétel változó a jelenleg kezelt kivételt jelenti. Határozott hozzárendelés-ellenőrzés céljából a kivételváltozó a teljes hatókörében egyértelműen hozzárendeltnek minősül.
Ha egy catch záradék nem tartalmaz kivételváltozónevet, nem lehet elérni a kivételobjektumot a szűrőben és catch a blokkban.
A catch záradék, amely sem kivételtípust, sem kivételváltozó nevet nem határoz meg, általános catch záradéknak nevezzük. Egy try utasítás csak egy általános catch záradékkal rendelkezhet, és ha van ilyen, akkor az utolsó catch záradék.
Megjegyzés: Egyes programozási nyelvek támogathatják azokat a kivételeket, amelyek nem jelölhetők olyan objektumként, amelyből
System.Exceptionszármazik, bár ezek a kivételek soha nem hozhatók létre C#-kóddal. Az ilyen kivételek elfogására általánoscatchzáradék használható. Az általánoscatchzáradék tehát szemantikailag eltér a típustSystem.Exceptionmeghatározótól, mivel az előbbi más nyelvek kivételeit is kifoghatja. végjegyzet
Ahhoz, hogy megtaláljuk a kivétel kezelőjét, a catch záradékokat lexikális sorrendben vizsgáljuk. Ha egy catch záradék típust ad meg, de nem tartalmaz kivételszűrőt, fordítási idejű hiba lesz, ha ugyanazon catch utasítás egy későbbi try záradéka olyan típust határoz meg, amely megegyezik az adott típussal, vagy abból származik.
Megjegyzés: E korlátozás nélkül elérhetetlen
catchzáradékok írhatók. végjegyzet
Egy catch blokkon belül egy throw kifejezés nélküli utasítás (§13.10.6) használható a catch blokk által elkapott kivétel újbóli eldobására. A kivételváltozóhoz való hozzárendelések nem módosítják az újradobott kivételt.
Példa: Az alábbi kódban
class Test { static void F() { try { G(); } catch (Exception e) { Console.WriteLine("Exception in F: " + e.Message); e = new Exception("F"); throw; // re-throw } } static void G() => throw new Exception("G"); static void Main() { try { F(); } catch (Exception e) { Console.WriteLine("Exception in Main: " + e.Message); } } }a metódus
Fkivételt kap, diagnosztikai adatokat ír a konzolra, módosítja a kivételváltozót, és újra eldobja a kivételt. Az újradobott kivétel az eredeti kivétel, így a létrehozott kimenet a következő:Exception in F: G Exception in Main: GHa az első
catchblokk az aktuális kivétel újradobása helyettedobott volna, az előállított kimenet a következő lenne:Exception in F: G Exception in Main: Fzáró példa
Fordítási időhiba, ha egy break, continue vagy goto utasítás átadja a vezérlést egy finally blokkon kívülre. Ha egy break, continue, vagy goto utasítás előfordul egy finally blokkban, akkor az utasítás célja ugyanabban a finally blokkban kell legyen, vagy ellenkező esetben fordítási hiba lép fel.
Fordítási idő hibát jelent, ha egy return utasítás egy finally blokkban fordul elő.
Amikor a végrehajtás eléri az utasítást try , a vezérlő átkerül a try blokkba. Ha a vezérlőelem kivétel propagálása nélkül éri el a try blokk végpontját, a vezérlőt a rendszer átviszi a finally blokkba, ha létezik ilyen. Ha nincs finally blokk, a vezérlő átkerül az utasítás végpontjára try .
Ha egy kivétel propagálása megtörtént, a catch záradékok ( ha vannak ilyenek) lexikális sorrendben vannak megvizsgálva, és a kivétel első egyezését keresik. Az egyező catch záradék keresése folytatódik minden környező blokkra, ahogyan az a §13.10.6 részben le van írva. A catch záradékok akkor egyeznek, ha a kivétel típusa megegyezik bármely exception_specifier , és minden exception_filter igaz. A catch exception_specifier nélküli záradék minden kivételtípusnak megfelel. A kivétel típusa megegyezik a exception_specifier , amikor a exception_specifier megadja a kivétel típusát vagy a kivételtípus alaptípusát. Ha a záradék kivételszűrőt tartalmaz, a kivételobjektum hozzá lesz rendelve a kivételváltozóhoz, és kiértékeli a kivételszűrőt. Ha egy exception_filter boolean_expression kiértékelése kivételt eredményez, a rendszer kivételt kap, és a kivételszűrő kiértékeli a következőtfalse: .
Ha egy kivétel propagálása és egyező catch záradék található, a vezérlő átkerül az első egyező catch blokkba. Ha a vezérlőelem kivétel propagálása nélkül éri el a catch blokk végpontját, a vezérlőt a rendszer átviszi a finally blokkba, ha létezik ilyen. Ha nincs finally blokk, a vezérlő átkerül az utasítás végpontjára try . Ha egy kivételt propagált a catch blokkból, akkor a vezérlő átkerül a finally blokkba, ha van ilyen. A kivételt a rendszer a következő belefoglaló try utasításba propagálja.
Ha egy kivétel propagálása megtörtént, és nem található egyező catch záradék, akkor a vezérlőelem átviszi a finally blokkba, ha létezik. A kivételt a rendszer a következő belefoglaló try utasításba propagálja.
A finally blokk utasításai mindig végrehajtásra kerülnek, amikor a vezérlés elhagy egy try utasítást. Ez igaz arra, hogy a vezérlőátvitel a normál végrehajtás eredményeként, egy break, , continue, gotovagy return utasítás végrehajtása vagy egy kivétel utasításból try való propagálása eredményeként történik. Ha a vezérlőelem kivétel propagálása nélkül éri el a finally blokk végpontját, a vezérlő átkerül az try utasítás végpontjára.
Ha egy finally blokk végrehajtása közben egy kivétel keletkezik, és azt nem kapják el ugyanabban a finally blokkban, akkor a kivétel a következő try utasításhoz terjed tovább. Ha egy másik kivétel propagálása folyamatban volt, a kivétel elveszik. A kivétel propagálásának folyamatát a nyilatkozat leírásában throw (13.10.6. §) tárgyaljuk.
Példa: Az alábbi kódban
public class Test { static void Main() { try { Method(); } catch (Exception ex) when (ExceptionFilter(ex)) { Console.WriteLine("Catch"); } bool ExceptionFilter(Exception ex) { Console.WriteLine("Filter"); return true; } } static void Method() { try { throw new ArgumentException(); } finally { Console.WriteLine("Finally"); } } }a metódus
Methodkivételt jelez. Az első művelet a körülvevőcatchzáradékok vizsgálata, a kivételszűrők végrehajtása. Ezután afinallyzáradékMethodvégrehajtódik, mielőtt a vezérlés átkerülne a belefoglaló egyezőcatchzáradékba. Az eredmény a következő:Filter Finally Catchzáró példa
Az try utasítás blokkja try akkor érhető el, ha az try utasítás elérhető.
Az catch utasítás blokkja try akkor érhető el, ha az try utasítás elérhető.
Az finally utasítás blokkja try akkor érhető el, ha az try utasítás elérhető.
Az utasítás végpontja try akkor érhető el, ha az alábbiak mindegyike igaz:
- A blokk végpontja
tryelérhető, vagy legalább egycatchblokk végpontja elérhető. - Ha egy
finallyblokk jelen van, a blokk végpontjafinallyelérhető.
13.12 Az ellenőrzött és a nem ellenőrzött utasítások
Az checked és az utasítások az integrál típusú aritmetikai műveletek és unchecked átalakítások túlcsordulás-ellenőrzési környezetének szabályozására szolgálnak.
checked_statement
: 'checked' block
;
unchecked_statement
: 'unchecked' block
;
Az checked utasítás hatására a blokk összes kifejezését ki kell értékelni egy ellenőrzött környezetben, az unchecked utasítás pedig azt eredményezi, hogy a blokk összes kifejezése ellenőrizetlen környezetben lesz kiértékelve.
Az checked és unchecked utasítások pontosan egyenértékűek a checked és unchecked operátorokkal (§12.8.20), azzal a különbséggel, hogy a kifejezések helyett blokkokon működnek.
13.13 A zárolási utasítás
Az lock utasítás lekérte egy adott objektum kölcsönös kizárási zárolását, végrehajt egy utasítást, majd feloldja a zárolást.
lock_statement
: 'lock' '(' expression ')' embedded_statement
;
A kifejezés egy lock állításnak olyan típusú értéket kell jelölnie, amely ismerten hivatkozás. Soha nem végezhető el implicit "boxing" konverzió (§10.2.9) egy utasítás kifejezésére, ezért fordítási idejű hibát okoz, ha a kifejezés egy lock típusú értéket jelöl.
Az lock űrlap nyilatkozata
lock (x) …
ahol x egy reference_type kifejezése, pontosan egyenértékű a következővel:
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(x, ref __lockWasTaken);
...
}
finally
{
if (__lockWasTaken)
{
System.Threading.Monitor.Exit(x);
}
}
kivéve, hogy csak x egyszer van kiértékelve.
Míg a kölcsönös kizárási zárolást tartják, az ugyanabban a végrehajtási szálban végrehajtó kód is beszerezheti és feloldhatja a zárolást. A többi szálon futtatott kód azonban a zárolás feloldásáig nem lesz elérhető.
13.14 A használat utasítása
13.14.1 Általános
Az using utasítás lekért egy vagy több erőforrást, végrehajt egy utasítást, majd megsemmisíti az erőforrást.
using_statement
: 'await'? 'using' '(' resource_acquisition ')' embedded_statement
;
resource_acquisition
: non_ref_local_variable_declaration
| expression
;
non_ref_local_variable_declaration
: implicitly_typed_local_variable_declaration
| explicitly_typed_local_variable_declaration
;
Az erőforrástípus vagy osztály vagy nem refstruktúra System.IDisposable , amely vagy mindkettőt vagy System.IAsyncDisposable interfészt implementál, amely egyetlen paraméter nélküli metódust tartalmaz és Dispose /vagy DisposeAsync; vagy egy refstructot, amely egy olyan metódust tartalmaz, amelynek az aláírása Dispose megegyezik a deklarálttal System.IDisposable. Az erőforrást használó kód meghívhatja Dispose vagy DisposeAsync jelezheti, hogy az erőforrásra már nincs szükség.
Ha a resource_acquisition formája local_variable_declaration , akkor a local_variable_declaration típusának vagy dynamic erőforrástípusnak kell lennie. Ha a resource_acquisition kifejezés , akkor ennek a kifejezésnek erőforrástípussal kell rendelkeznie. Ha await jelen van, az erőforrástípusnak implementálnia System.IAsyncDisposablekell . A ref struct módosítóval rendelkező using utasítás erőforrástípusa await nem lehet típus.
A resource_acquisition utasításban deklarált helyi változók csak olvashatóak, és inicializátort kell tartalmazniuk. Fordítási időhiba akkor fordul elő, ha a beágyazott utasítás megkísérli módosítani ezeket a helyi változókat (hozzárendeléssel vagy operátorokkal ++-- ), átveszi azok címét, vagy referencia- vagy kimeneti paraméterként adja át őket.
Az using utasítás három részre van lefordítva: beszerzés, használat és ártalmatlanítás. Az erőforrás használata egy olyan utasításba van foglalva, amely try záradékot tartalmaz, beleértve finally. Ez a finally záradék megsemmisíti az erőforrást. Ha a beszerzési kifejezés kiértékelése így történik null, akkor nem történik hívás Dispose (vagy DisposeAsync) a rendszer, és nem történik kivétel. Ha az erőforrás típusa dynamic implicit dinamikus átalakítás (10.2.10.§) IDisposable és (vagy IAsyncDisposable) lesz a beszerzés során, annak érdekében, hogy az átalakítás sikeres legyen a használat és az elidegenítés előtt.
Az using űrlap nyilatkozata
using (ResourceType resource = «expression» ) «statement»
három lehetséges készítmény egyikének felel meg. Osztály- és nem újrastrukturálási erőforrások esetében, ha ResourceType nem null értékű értéktípus vagy értéktípus-korlátozással rendelkező típusparaméter (15.2.5. §) a készítmény szemantikailag egyenértékű
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
((IDisposable)resource).Dispose();
}
}
kivéve, hogy a dobás resourceSystem.IDisposable nem okozhatja az ökölvívást.
Ellenkező esetben, ha ResourceType van dynamic, a készítmény
{
ResourceType resource = «expression»;
IDisposable d = resource;
try
{
«statement»;
}
finally
{
if (d != null)
{
d.Dispose();
}
}
}
Ellenkező esetben a készítmény
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
IDisposable d = (IDisposable)resource;
if (d != null)
{
d.Dispose();
}
}
}
Az erőforrások újrastrukturálásához az egyetlen szemantikailag egyenértékű készítmény a
{
«ResourceType» resource = «expression»;
try
{
«statement»;
}
finally
{
resource.Dispose();
}
}
Bármely megfogalmazásban a resource változó írásvédett a beágyazott utasításban, a d változó pedig nem érhető el és nem látható a beágyazott utasításban.
Az using űrlap egy nyilatkozata:
using («expression») «statement»
ugyanazokkal a lehetséges készítményekkel rendelkezik.
Ha egy erőforrás-beszerzéslokális változó deklarációja formájában történik, több erőforrást is szerezhetünk be egy adott típusra vonatkozóan. Az using űrlap nyilatkozata
using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) «statement»
pontosan egyenértékű a beágyazott using utasítások sorozatával:
using (ResourceType r1 = e1)
using (ResourceType r2 = e2)
...
using (ResourceType rN = eN)
«statement»
Példa: Az alábbi példa létrehoz egy log.txt nevű fájlt, és két sornyi szöveget ír a fájlba. A példa ezután megnyitja ugyanazt a fájlt olvasáshoz, és átmásolja a tartalmazott szövegsorokat a konzolra.
class Test { static void Main() { using (TextWriter w = File.CreateText("log.txt")) { w.WriteLine("This is line one"); w.WriteLine("This is line two"); } using (TextReader r = File.OpenText("log.txt")) { string s; while ((s = r.ReadLine()) != null) { Console.WriteLine(s); } } } }Mivel az és
TextWriterazTextReaderosztályok implementálják azIDisposableinterfészt, a példa utasításokkal biztosíthatjausing, hogy a mögöttes fájl megfelelően legyen lezárva az írási vagy olvasási műveletek után.záró példa
Amikor ResourceType egy referenciatípus implementálható IAsyncDisposable. Más készítmények a await using szinkron Dispose módszertől az aszinkron DisposeAsync módszerig hasonló helyettesítések elvégzésére. Az await using űrlap nyilatkozata
await using (ResourceType resource = «expression» ) «statement»
szemantikailag egyenértékű az alábbiakban bemutatott készítményekkel IAsyncDisposableIDisposable, és ahelyett, DisposeAsyncDispose hogy a Task visszaadott készítményeket a következőkből DisposeAsync nyerik awaitki:
await using (ResourceType resource = «expression» ) «statement»
szemantikailag egyenértékű a
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
IAsyncDisposable d = (IAsyncDisposable)resource;
if (d != null)
{
await d.DisposeAsync();
}
}
}
Megjegyzés: A embedded_statement minden ugrási utasításnak (13.10.§) meg kell felelnie az utasítás kibontott formájának
using. végjegyzet
13.14.2 Deklaráció használata
A using utasítás szintaktikai változata egy használat deklaráció.
using_declaration
: 'await'? 'using' non_ref_local_variable_declaration ';' statement_list?
;
A használó deklarációk szemantikája megegyezik a felhasználói utasítás megfelelő erőforrás-beszerzési formájával (13.14.1. §), az alábbiak szerint:
using «local_variable_type» «local_variable_declarators»
// statements
szemantikailag egyenértékű a
using («local_variable_type» «local_variable_declarators»)
{
// statements
}
és
await using «local_variable_type» «local_variable_declarators»
// statements
szemantikailag egyenértékű a
await using («local_variable_type» «local_variable_declarators»)
{
// statements
}
A non_ref_local_variable_declaration deklarált változók élettartama a deklarált hatókör végéig terjed. Ezeket a változókat ezután a deklarálandó fordított sorrendben kell megsemmisíteni.
static void M()
{
using FileStream f1 = new FileStream(...);
using FileStream f2 = new FileStream(...), f3 = new FileStream(...);
...
// Dispose f3
// Dispose f2
// Dispose f1
}
A felhasználói nyilatkozat nem jelenhet meg közvetlenül egy switch_label belül, hanem egy switch_label belüli blokkon belül lehet.
13.15 A hozamkimutatás
Az yield utasítást egy iterátorblokkban (13.3. §) használjuk, hogy értéket adjon az iterátor enumerátorobjektumának (15.15.5. §) vagy egy iterátor számba vett objektumának (15.15.6. §) vagy az iteráció végének jelzésére.
yield_statement
: 'yield' 'return' expression ';'
| 'yield' 'break' ';'
;
yieldkörnyezetfüggő kulcsszó (6.4.4.4. §), és csak akkor jelent különleges jelentést, ha közvetlenül egy vagy return több break kulcsszó előtt használják.
Az utasítás megjelenési yield helye több korlátozást is tartalmazhat, az alábbiakban leírtak szerint.
- Fordítási időhiba, ha egy
yieldutasítás (mindkét formában) egy method_body, operator_body vagy accessor_body kívül jelenik meg. - Fordítási időhiba, ha egy
yieldutasítás (mindkét formában) egy névtelen függvényben jelenik meg. - Fordítási idejű hiba, ha egy
yieldutasítás (mindkét formában) megjelenik egyfinallyzáradékában egytryutasításnak. - Fordítási idejű hiba, ha egy
yield returnutasítás bárhol megjelenik egytryutasításban, amely tartalmaz bármilyen catch_clauses-t.
Példa: Az alábbi példa az utasítások néhány érvényes és érvénytelen használatát
yieldmutatja be.delegate IEnumerable<int> D(); IEnumerator<int> GetEnumerator() { try { yield return 1; // Ok yield break; // Ok } finally { yield return 2; // Error, yield in finally yield break; // Error, yield in finally } try { yield return 3; // Error, yield return in try/catch yield break; // Ok } catch { yield return 4; // Error, yield return in try/catch yield break; // Ok } D d = delegate { yield return 5; // Error, yield in an anonymous function }; } int MyMethod() { yield return 1; // Error, wrong return type for an iterator block }záró példa
Implicit átalakításnak (10.2. §) kell léteznie az utasításban szereplő yield return kifejezés típusától az iterátor hozamtípusára (15.15.4. §).
Az yield return utasítás végrehajtása az alábbiak szerint történik:
- Az utasításban megadott kifejezés kiértékelése, implicit módon a hozamtípussá alakítása és az
Currentenumerátor objektum tulajdonságához van rendelve. - Az iterátorblokk végrehajtása fel van függesztve. Ha az
yield returnutasítás egy vagy többtryblokkon belül van, a társítottfinallyblokkok végrehajtása jelenleg nem történik meg. - Az
MoveNextenumerátor objektum metódusa visszatértruea hívóhoz, ami azt jelzi, hogy az enumerátor objektum sikeresen továbbhaladt a következő elemre.
Az enumerátorobjektum metódusának következő hívása MoveNext folytatja az iterátorblokk végrehajtását, ahonnan az utolsó felfüggesztésre került.
Az yield break utasítás végrehajtása az alábbiak szerint történik:
- Ha az
yield breakutasítást egy vagy többtry, társítottfinallyblokkot tartalmazó blokk zárja be, a vezérlő kezdetben átkerül afinallylegbelsőtryutasítás blokkjába. Amikor és ha a vezérlőelem eléri afinallyblokk végpontját, a vezérlő átkerül afinallykövetkező zárótryutasítás blokkjába. Ez a folyamat addig ismétlődik, amíg azfinallyösszes belefoglalótryutasítás blokkjait végre nem hajtják. - A rendszer visszaadja a vezérlőt az iterátorblokk hívójának. Ez az
MoveNextenumerátor objektum metódusa vagyDisposemetódusa.
Mivel egy yield break utasítás feltétel nélkül átviszi az irányítást máshová, az utasítás végpontja yield break soha nem érhető el.
ECMA C# draft specification