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 (23.2. §) és fixed_statement (23.7. §) csak nem biztonságos kódban (23. §) é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
if
utasí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ói
deklarálva lesz, de soha nem használható. Azonban vegye figyelembe, hogy a deklaráció blokkon belüli elhelyezésével ai
pé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.23.§) é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.WriteLine
meghívás elérhetetlennek minősül. Hai
azonban helyi változóként van módosítvavoid F() { int i = 1; if (i == 2) Console.WriteLine("reachable"); }
a
Console.WriteLine
meghí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.WriteLine
elérhetőségét az alábbiak szerint határozzuk meg:
- Az első
Console.WriteLine
kifejezési utasítás azért érhető el, mert aF
metódus blokkja elérhető (13.3. §).- Az első
Console.WriteLine
kifejezési utasítás végpontja azért érhető el, mert ez az utasítás elérhető (13.7. és 13.3. §).- Az
if
utasítás azért érhető el, mert az elsőConsole.WriteLine
kifejezési utasítás végpontja elérhető (§13.7 és §13.3).- A második
Console.WriteLine
kifejezési utasítás azért érhető el, mert az utasítás logikai kifejezésénekif
nincs á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
switch
utasí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 egybreak
utasí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
return
utasí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
return
utasítás megjelenik egy iterátorblokkban (deyield return
utasítások engedélyezettek). - Fordítási idejű hiba, ha egy iterátorblokk nem biztonságos környezetet tartalmaz (§23.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ő
goto
utasí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
goto
utasí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
var
tí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
var
tí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) részeként fordul elő, ez a balról jobbra történő sorrend egyenértékű azzal, hogy minden deklarátor külön local_variable_declaration belül van. 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 ref
variable_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 itself
zá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 explicity_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.21.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_reference típusú type kell legyen, és meg kell felelnie a ref hozzárendelés (§12.21.3) követelményeinek.
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 egy azonosítóból áll, amely elnevezi az állandót, ezt követi egy „” token, majd egy =
(12.23. §), amely az állandó értékét adja meg.
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ót és egy unsafe
(§23.1) módosító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 (23.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
this
ésgoto
szabályok tükrözik a névtelen függvények szabályait a §12.19.3-ban. 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
L
akkor is elérhető, ha a kezdőpontL
nem érhető el. Mivel a kezdőpontL
nem érhető el, a végpontotL
kö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
this
vagybase
nem hozhat létre példánytagokat implicitthis
hivatkozá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 + y
pé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
if
formá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.24. §) 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 azif
utasítás végpontjára. - Ha a logikai kifejezés hozamot ad
false
, és egyelse
ré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 azif
utasítás végpontjára. - Ha a logikai kifejezés hozamot ad
false
, és ha egyelse
rész nincs jelen, a vezérlőelem átkerül azif
utasí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ókifejezés értékének megfelelő kapcsolócímkével rendelkezik.
switch_statement
: 'switch' '(' expression ')' switch_block
;
switch_block
: '{' switch_section* '}'
;
switch_section
: switch_label+ statement_list
;
switch_label
: 'case' pattern case_guard? ':'
| 'default' ':'
;
case_guard
: 'when' expression
;
A switch_statement a kulcsszóból switch
, majd egy zárójeles kifejezésből (más néven kapcsolókifejezésből) áll, amelyet egy switch_block követ. 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ókifejezés értékét teszteli. 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.
Az utasítás szabályozási típusátswitch
a kapcsolókifejezés határozza meg.
- Ha a kapcsolókifejezés
sbyte
típusa ,byte
,short
,ushort
,int
,uint
long
,ulong
,char
,bool
string
vagy egy enum_type, vagy ha az egyik ilyen típusnak megfelelő null értékű érték, akkor ez azswitch
utasítás szabályozási típusa. - Ellenkező esetben, ha pontosan egy felhasználó által definiált implicit konverzió létezik a kapcsolókifejezés típusából a következő lehetséges szabályozási típusok egyikére:
sbyte
, , ,byte
,short
ushort
int
uint
long
ulong
,char
, , vagy egy ilyen típusnak megfelelő null értékű értéktípus, akkor az átalakított típus azstring
utasítás szabályozási típusa.switch
- Ellenkező esetben a
switch
utasítás irányító típusa a kapcsolókifejezés 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ókifejezés kiértékelése és átalakítása a szabályozási típusra történik.
- A vezérlő a konvertált kapcsolókifejezés értékének megfelelően lesz átadva:
- A
case
címkék halmazában a lexikálisan első minta ugyanabban aswitch
utasításban, amely megegyezik a kapcsolókifejezés értékével, és amelynél az őrkifejezés hiányzik vagy igazra értékelődik, a vezérlést a megfelelőcase
címkét követő utasításlistára irányítja. - Ellenkező esetben, ha egy
default
címke jelen van, a vezérlő a címkét követődefault
utasításlistára kerül. - Ellenkező esetben a vezérlőelem átkerül az utasítás végpontjára
switch
.
- A
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 case
vagygoto default
utasí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
break
az utasítások véletlenül kimaradnak. A fenti utasítás szakaszaiswitch
pé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 case
vagygoto default
utasí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áultrue
ismert, hogy soha nem éri el a végpontját. Hasonlóképpen, egythrow
vagyreturn
utasí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
switch
lehet 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.12.8) hasonlóan, a
switch
utasítás megkülönbözteti a kis- és nagybetűket, és csak akkor hajt végre egy adott kapcsolószakaszt, ha a kapcsolókifejezés sztringje pontosan egyezik acase
címkeállandóval. végjegyzet Ha egyswitch
utasításstring
típusa referencia típus vagy nullálható érték típus, akkor az értéknull
engedélyezettcase
címkeállandóként.
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ókifejezés egy állandó érték, amely vagy
- a címke egy
case
olyan 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
default
cí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ókifejezés 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
default
cí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 case
vagygoto default
utasí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
switch
utasítás egy elérhetőbreak
utasítást tartalmaz, amely kilép aswitch
utasításból. - Nincs
default
címke, és egyik sem- A kapcsolókifejezés nem állandó érték, és a kapcsolóutasítás olyan esetei között megjelenő minták, amelyek nem rendelkeznek védőkkel vagy olyan védőkkel, amelyek értéke az állandó igaz, nem teljes (11.4. §) a kapcsoló szabályozási típusára vonatkozóan.
- A kapcsolókifejezés egy nullázható típusú, nem állandó érték, és nincs olyan minta a kapcsolóutasítás esetei között, amelyek őrök nélküliek, vagy ahol az őrök értéke az állandó igaz. Így egyik eset sem felel meg az
null
értéknek. - A kapcsolókifejezés állandó érték, és nincs
case
olyan címke, amely nem rendelkezik védővel, vagy amelynek az őre 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
when
mutatja 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
null
karakterlá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.24. §) 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 egycontinue
utasítás végrehajtásából), a vezérlő átkerül azwhile
utasítás elejére. - Ha a logikai kifejezés hozamot eredményez
false
, a vezérlőelem átkerül azwhile
utasí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
while
utasítás egy elérhetőbreak
utasítást tartalmaz, amely kilép awhile
utasításból. - Az
while
utasí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.
- Amikor és ha a vezérlés eléri a beágyazott utasítás végpontját (lehetséges egy
continue
utasítás végrehajtását követően), a boolean_expression (§12.24) kiértékelésre kerül. Ha a logikai kifejezés hozamot adtrue
, a vezérlőelem átkerül azdo
utasí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
do
utasítás egy elérhetőbreak
utasítást tartalmaz, amely kilép ado
utasí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 jelen van, boolean_expression (§12.24) kell legyen.
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 egycontinue
utasí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 azfor
utasí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
for
kijelentés elérhető, és nincs for_condition. - Az
for
utasí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
for
utasítás egy elérhetőbreak
utasítást tartalmaz, amely kilép afor
utasításból. - Az
for
utasí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 ref
readonly
tartalmazza, 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 idejének foreach
feldolgozása először meghatározza a kifejezés gyűjteménytípusát, enumerátortípusát és iterációs típusát. Az utasítás foreach
feldolgozását a 13.9.5.2. § részletezi, és a await foreach
feldolgozását a 13.9.5.3.
Megjegyzés: Ha a kifejezés értéke
null
van, a rendszer futásidőben ad ki egySystem.NullReferenceException
értéket. végjegyzet
Az implementációk eltérő módon implementálhatnak egy adott foreach_statement , például teljesítménybeli okokból, amennyiben a viselkedés összhangban van a fenti bővítéssel.
13.9.5.2 Szinkron foreach
Az utasítás fordítási idejének foreach
feldolgozása először meghatározza a kifejezés gyűjteménytípusát, enumerátortípusát és iterációs típusát. Ez a meghatározás a következőképpen folytatódik:
- Ha a
X
típusa tömbtípus, akkor implicit referenciaátalakítás van X-ről aIEnumerable
interfészre (mivelSystem.Array
implementálja ezt az interfészt). A gyűjtemény típusa azIEnumerable
interfész, az enumerátor típusa azIEnumerator
interfész, az iteráció típusa pedig a tömbtípus elemtípusaX
. - Ha a
X
típusa , akkor van egy implicit átalakítás adynamic
a interfészre (IEnumerable
). A gyűjtemény típusa azIEnumerable
interfész, az enumerátor típusa pedig azIEnumerator
interfész. Ha avar
azonosítót local_variable_typeként adjuk meg, akkor az iteráció típusadynamic
; ellenkező esetben a(z)object
. - Egyéb esetben határozza meg, hogy a típus
X
rendelkezik-e megfelelőGetEnumerator
módszerrel:- Tagkeresés végrehajtása a típuson
X
azonosítóvalGetEnumerator
é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, keressen egy számbavételi felületet az alábbiak szerint. 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
E
metódus visszatérési típusaGetEnumerator
nem osztály, struktúra vagy illesztőtípus, a rendszer hibát okoz, és nem történik további lépés. - A tagkeresés a
E
elemre történik azonosítóvalCurrent
és típusérv nélkül. Ha a tagkeresés nem hoz létre egyezést, az eredmény hiba, vagy az eredmény nem más, mint egy olvasást lehetővé tevő nyilvános példánytulajdonság, hiba keletkezik, és nem történik további lépés. - A tagkeresés a
E
elemre történik azonosítóvalMoveNext
és típusérv nélkül. Ha a tagkeresés nem hoz létre egyezést, az eredmény hiba, vagy az eredmény egy metóduscsoport kivételével bármi, hiba keletkezik, és nem történik további lépés. - A túlterhelés feloldása üres argumentumlistával történik a metóduscsoporton. 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, vagy a visszatérési típusa nem
bool
, hiba keletkezik, és nem történik további lépés. - A gyűjtemény típusa
X
, az enumerátor típusaE
, az iteráció típusa pedig aCurrent
tulajdonság típusa. ACurrent
tulajdonság tartalmazhatja aref
módosítót, ebben az esetben a visszaadott kifejezés egy variable_reference (§9.5), amely opcionálisan írásvédett.
- Tagkeresés végrehajtása 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ójaX
IEnumerable<Tᵢ>
van, van egy egyedi típusT
, amelyT
nemdynamic
, és az összes többiTᵢ
esetében implicit konverzióIEnumerable<T>
IEnumerable<Tᵢ>
van a másikra, akkor a gyűjtemény típusa az interfészIEnumerable<T>
, az enumerátor típusa az interfészIEnumerator<T>
, és az iteráció típusaT
. - Ellenkező esetben, ha egynél több ilyen típus
T
van, akkor a rendszer hibát okoz, és nem hajt végre további lépéseket. - Ellenkező esetben, ha implicit átalakítás történik a felületről a
X
felületreSystem.Collections.IEnumerable
, akkor a gyűjtemény típusa ez az interfész, az enumerátor típusa az interfészSystem.Collections.IEnumerator
, az iteráció típusa pedig azobject
. - Ellenkező esetben hibaüzenet jelenik meg, és nem történik további lépés.
- Ha az összes típus
A fenti lépések, ha sikeresek, egyértelműen létrehoznak egy gyűjteménytípust C
, enumerátortípust E
és iterációtípust T
, ref T
vagy ref readonly T
. 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 v
referenciaváltozó a beágyazott utasításban írható-olvasható, de a v
nem lehet újra átirányítva (§12.21.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 v
elhelyezése a while
cikluson belül fontos annak érdekében, hogy a rendszer hogyan rögzíti (§12.19.6.2) az embedded_statement-ben előforduló névtelen függvény á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
v
a kibontott formában awhile
cikluson kívül deklarálnák, akkor az minden iteráció között meg lesz osztva, és afor
ciklus utáni értéke a végső érték lenne,13
amit a hívásaf
nyomtatna ki. Ehelyett, mivel minden iteráció saját változóvalv
rendelkezik, az első iterációban rögzítettf
érték továbbra is az értéket7
fogja tárolni, ami a nyomtatni fog. (Vegye figyelembe, hogy a C# korábbi verziói av
cikluson 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
E
aSystem.IDisposable
felületre, akkorHa
E
nem null értékű, akkor afinally
záradék a következő szemantikai megfelelője lesz:finally { ((System.IDisposable)e).Dispose(); }
Ellenkező esetben a
finally
zá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
E
egy értéktípus vagy egy értéktípusra példányosított típusparaméter, akkore
konvertálásaSystem.IDisposable
-vé nem okozhat boxolást.
Ellenkező esetben, ha
E
lezárt típus, afinally
zá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 – 1
vé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.9
zá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); }
n
típusáról kikövetkeztették, hogyint
, aminumbers
iterációtípusa.záró példa
13.9.5.3 kivár foreach
Az utasítás fordítási idejének foreach
feldolgozása először meghatározza a kifejezés gyűjteménytípusát, enumerátortípusát és iterációs típusát. Az utasítás foreach
feldolgozását a 13.9.5.2. § részletezi, és a await foreach
feldolgozását a 13.9.5.3.
Ez a meghatározás a következőképpen folytatódik:
- Határozza meg, hogy a típus
X
rendelkezik-e megfelelőGetAsyncEnumerator
módszerrel:- Tagkeresés végrehajtása a típuson
X
azonosítóvalGetAsyncEnumerator
é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, keressen egy számbavételi felületet az alábbiak szerint. 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
E
metódus visszatérési típusaGetAsyncEnumerator
nem osztály, struktúra vagy illesztőtípus, a rendszer hibát okoz, és nem történik további lépés. - A tagkeresés a
E
elemre történik azonosítóvalCurrent
és típusérv nélkül. Ha a tagkeresés nem hoz létre egyezést, az eredmény hiba, vagy az eredmény nem más, mint egy olvasást lehetővé tevő nyilvános példánytulajdonság, hiba keletkezik, és nem történik további lépés. - A tagkeresés a
E
elemre történik azonosítóvalMoveNextAsync
és típusérv nélkül. Ha a tagkeresés nem hoz létre egyezést, az eredmény hiba, vagy az eredmény egy metóduscsoport kivételével bármi, hiba keletkezik, és nem történik további lépés. - A túlterhelés feloldása üres argumentumlistával történik a metóduscsoporton. Ha a túlterhelés feloldása nem eredményez alkalmazható módszereket, kétértelműséget eredményez, vagy egyetlen legjobb módszert eredményez, de ez a módszer statikus vagy nem nyilvános, vagy a visszatérési típusa nem várható el (12.9.8.2. §), ha a await_expression besorolása
bool
(12.9.8.3. §), hiba keletkezik, és további lépések nem történnek. - A gyűjtemény típusa
X
, az enumerátor típusaE
, az iteráció típusa pedig aCurrent
tulajdonság típusa.
- Tagkeresés végrehajtása a típuson
- Ellenkező esetben ellenőrizze, hogy van-e aszinkron számbavételi felület:
- Ha az összes típus
Tᵢ
között, amelynek implicit konverziójaX
IAsyncEnumerable<Tᵢ>
van, van egy egyedi típusT
, amelyT
nemdynamic
, és az összes többiTᵢ
esetében implicit konverzióIAsyncEnumerable<T>
IAsyncEnumerable<Tᵢ>
van a másikra, akkor a gyűjtemény típusa az interfészIAsyncEnumerable<T>
, az enumerátor típusa az interfészIAsyncEnumerator<T>
, és az iteráció típusaT
. - Ellenkező esetben, ha egynél több ilyen típus
T
van, akkor a rendszer hibát okoz, és nem hajt végre további lépéseket. - Ellenkező esetben hibaüzenet jelenik meg, és nem történik további lépés.
- Ha az összes típus
Ha a fenti lépések sikeresek, egyértelműen létrehoznak egy gyűjteménytípust C
, enumerátortípust E
és iterációs típust T
. Az await foreach
űrlap nyilatkozata
await foreach (V v in x) «embedded_statement»
akkor a következőnek felel meg:
{
E e = ((C)(x)).GetAsyncEnumerator();
try
{
while (await e.MoveNextAsync())
{
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 await foreach
utasításban) között, hibaüzenet jelenik meg, és nem történik további lépés.
Az aszinkron enumerátor opcionálisan közzétehet egy DisposeAsync
metódust, amely argumentumok nélkül hívható meg, és amely visszaad egy olyan értéket, amelyre elvégezhető a await
, és amelynek a GetResult()
metódusa egy void
-t ad vissza.
Az foreach
űrlap nyilatkozata
await foreach (T item in enumerable) «embedded_statement»
a következőre bővítették:
var enumerator = enumerable.GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync())
{
T item = enumerator.Current;
«embedded_statement»
}
}
finally
{
await enumerator.DisposeAsync(); // omitted, along with the try/finally,
// if the enumerator doesn't expose DisposeAsync
}
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
finally
utasításhoz társítotttry
blokkok 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 break
zá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
, for
vagy 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
break
utasítás kilép egy vagy többtry
blokkból a társítottfinally
blokkokkal, a vezérlő kezdetben afinally
legbelsőtry
utasítás blokkjába kerül. Amikor és ha a vezérlőelem eléri afinally
blokk végpontját, a vezérlő átkerül afinally
következő zárótry
utasítás blokkjába. Ez a folyamat addig ismétlődik, amíg azfinally
összes beavatkozótry
utasítás blokkjainak végrehajtása meg nem történik. - A vezérlés átkerül az
break
utasí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
, for
vagy 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
continue
utasítás kilép egy vagy többtry
blokkból a társítottfinally
blokkokkal, a vezérlő kezdetben afinally
legbelsőtry
utasítás blokkjába kerül. Amikor és ha a vezérlőelem eléri afinally
blokk végpontját, a vezérlő átkerül afinally
következő zárótry
utasítás blokkjába. Ez a folyamat addig ismétlődik, amíg azfinally
összes beavatkozótry
utasítás blokkjainak végrehajtása meg nem történik. - A vezérlés átkerül az
continue
utasí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 goto
azonosí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
goto
utasí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
goto
utasí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
goto
utasítás kilép egy vagy többtry
blokkból a társítottfinally
blokkokkal, a vezérlő kezdetben afinally
legbelsőtry
utasítás blokkjába kerül. Amikor és ha a vezérlőelem eléri afinally
blokk végpontját, a vezérlő átkerül afinally
következő zárótry
utasítás blokkjába. Ez a folyamat addig ismétlődik, amíg azfinally
összes beavatkozótry
utasítás blokkjainak végrehajtása meg nem történik. - A vezérlés átkerül az
goto
utasí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.19.§), és részt vehetnek annak meghatározásában, hogy mely átalakítások léteznek ezen függvények esetében (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
return
utasítást egy vagy többtry
vagycatch
blokk tartalmazza, amelyek társítottfinally
blokkokkal rendelkeznek, a vezérlés kezdetben a legbelsőfinally
utasítástry
blokkjába kerül. Amikor és ha a vezérlőelem eléri afinally
blokk végpontját, a vezérlő átkerül afinally
következő zárótry
utasítás blokkjába. Ez a folyamat addig ismétlődik, amíg azfinally
összes belefoglalótry
utasí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.Exception
kell 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 null
lesz, 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 (21.4. §) határozza meg.
Az aktuális függvénytagban minden
try
olyan utasítást megvizsgálunk, amely a dobáspontot tartalmazza. Minden utasításS
esetében a legbelsőtry
utasítástól kezdve a legkülsőtry
utasításig a következő lépések lesznek kiértékelve:- Ha a
try
blokkS
befogja a dobópontot, ésS
egy vagy többcatch
záradékkal rendelkezik, acatch
záradékok megjelenési sorrendben vannak megvizsgálva, hogy megtalálják a kivételhez megfelelő kezelőt. Az elsőcatch
záradék, amely egy kivételtípustT
határoz meg (vagy egy olyan típusparamétert, amely futásidőben kivételtípustT
jelöl), és amelybőlE
a futtatási idő típusaT
szá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 egycatch
záradék kivételszűrőt tartalmaz, akkor acatch
zá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őcatch
zá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
try
blokk vagy egycatch
blokkS
a dobópontot zárja be, és haS
van blokkjafinally
, akkor a vezérlő átkerül afinally
blokkba. Ha afinally
blokk egy másik kivételt ad ki, az aktuális kivétel feldolgozása leáll. Ellenkező esetben, ha a vezérlő eléri afinally
blokk 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.Exception
származik, bár ezek a kivételek soha nem hozhatók létre C#-kóddal. Az ilyen kivételek elfogására általánoscatch
záradék használható. Az általánoscatch
záradék tehát szemantikailag eltér a típustSystem.Exception
meghatá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
catch
zá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
F
kivé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: G
Ha az első
catch
blokk az aktuális kivétel újradobása helyette
dobott volna, az előállított kimenet a következő lenne:Exception in F: G Exception in Main: F
zá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 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
, goto
vagy 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
Method
kivételt jelez. Az első művelet a körülvevőcatch
záradékok vizsgálata, a kivételszűrők végrehajtása. Ezután afinally
záradékMethod
végrehajtódik, mielőtt a vezérlés átkerülne a belefoglaló egyezőcatch
záradékba. Az eredmény a következő:Filter Finally Catch
zá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
try
elérhető, vagy legalább egycatch
blokk végpontja elérhető. - Ha egy
finally
blokk jelen van, a blokk végpontjafinally
elé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
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
: 'using' '(' resource_acquisition ')' embedded_statement
;
resource_acquisition
: local_variable_declaration
| expression
;
Az erőforrás egy osztály vagy struktúra, amely implementálja az (System.IDisposable
aszinkron IAsyncDisposable
streamek) felületét, amely egyetlen paraméter nélküli metódust Dispose
tartalmaz (DisposeAsync
aszinkron streamekhez). Az erőforrást használó kód meghívhatja a Dispose
-t, így jelezve, 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át vagy olyan típust kell megadni dynamic
, amely implicit módon konvertálható System.IDisposable
(IAsyncDisposable
aszinkron streamekhez). Ha a resource_acquisition formája kifejezés , akkor ennek a kifejezésnek implicit módon konvertálhatónak System.IDisposable
kell lennie (IAsyncDisposable
aszinkron streamek esetén).
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 egy null
erőforrást megszereznek, akkor nem történik hívás a Dispose
(aszinkron streamek esetén a DisposeAsync
) függvényre, és nem dobódik kivétel. Ha az erőforrás típusa dynamic
, akkor az dinamikusan átalakul -vá egy implicit dinamikus átalakítás révén (IDisposable
), a beszerzés alatt, hogy az átalakítás sikeres legyen használat és ártalmatlanítás előtt, különösen IAsyncDisposable
aszinkron streamek esetén.
Az using
űrlap nyilatkozata
using (ResourceType resource = «expression» ) «statement»
három lehetséges bővítés egyikének felel meg. Ha ResourceType
nem null értékű értéktípus vagy értéktípus-korlátozással rendelkező típusparaméter (15.2.5. §), a bővítés szemantikailag egyenértékű a következővel:
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
((IDisposable)resource).Dispose();
}
}
kivéve, hogy a dobás resource
System.IDisposable
nem okozhatja az ökölvívást.
Ellenkező esetben, ha ResourceType
van dynamic
, a bővítés
{
ResourceType resource = «expression»;
IDisposable d = resource;
try
{
«statement»;
}
finally
{
if (d != null)
{
d.Dispose();
}
}
}
Ellenkező esetben a bővítés
{
ResourceType resource = «expression»;
try
{
«statement»;
}
finally
{
IDisposable d = (IDisposable)resource;
if (d != null)
{
d.Dispose();
}
}
}
Bármely bővítés esetén 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 implementációk eltérően implementálhatnak egy adott using_statement , például teljesítménybeli okokból, amennyiben a viselkedés összhangban van a fenti bővítéssel.
Az using
űrlap egy nyilatkozata:
using («expression») «statement»
ugyanaz a három lehetséges kiterjesztési lehetőség. Ebben az esetben ResourceType
implicit módon határozza meg a kifejezés fordítási időben érvényes típusát, ha van ilyen. Egyébként magát az interfészt IDisposable
(IAsyncDisposable
az aszinkron streamek esetében) önmagában mint ResourceType
használják. A resource
változó nem érhető el a beágyazott utasításban, és nem érhető el a beágyazott utasításban.
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
TextWriter
azTextReader
osztályok implementálják azIDisposable
interfé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
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' ';'
;
yield
kö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
yield
utasí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
yield
utasítás (mindkét formában) egy névtelen függvényben jelenik meg. - Fordítási idejű hiba, ha egy
yield
utasítás (mindkét formában) megjelenik egyfinally
záradékában egytry
utasításnak. - Fordítási idejű hiba, ha egy
yield return
utasítás bárhol megjelenik egytry
utasí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
yield
mutatja 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
Current
enumerátor objektum tulajdonságához van rendelve. - Az iterátorblokk végrehajtása fel van függesztve. Ha az
yield return
utasítás egy vagy többtry
blokkon belül van, a társítottfinally
blokkok végrehajtása jelenleg nem történik meg. - Az
MoveNext
enumerátor objektum metódusa visszatértrue
a 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 break
utasítást egy vagy többtry
, társítottfinally
blokkot tartalmazó blokk zárja be, a vezérlő kezdetben átkerül afinally
legbelsőtry
utasítás blokkjába. Amikor és ha a vezérlőelem eléri afinally
blokk végpontját, a vezérlő átkerül afinally
következő zárótry
utasítás blokkjába. Ez a folyamat addig ismétlődik, amíg azfinally
összes belefoglalótry
utasítás blokkjait végre nem hajtják. - A rendszer visszaadja a vezérlőt az iterátorblokk hívójának. Ez az
MoveNext
enumerátor objektum metódusa vagyDispose
metó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