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


13 Kijelentések

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 a i 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éket falseállítja elő, a Console.WriteLine meghívás elérhetetlennek minősül. Ha i azonban helyi változóként van módosítva

void 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 a F 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ének if nincs állandó értéke false.

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 egy break 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_list13.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 (de yield 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 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 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 és goto 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őpont L nem érhető el. Mivel a kezdőpont L nem érhető el, a végpontot L 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 vagy base nem hozhat létre példánytagokat implicit this 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ésben nameof() 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 és x == 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ában

if (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 az if utasítás végpontjára.
  • Ha a logikai kifejezés hozamot ad false , és egy else 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 az if utasítás végpontjára.
  • Ha a logikai kifejezés hozamot ad false , és ha egy else rész nincs jelen, a vezérlőelem átkerül az if 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 sbytetípusa , byte, short, ushort, int, uintlong, ulong, char, boolstringvagy egy enum_type, vagy ha az egyik ilyen típusnak megfelelő null értékű érték, akkor ez az switch 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, shortushortintuintlongulong, char, , vagy egy ilyen típusnak megfelelő null értékű értéktípus, akkor az átalakított típus az string 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 a switch 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 .

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 vagy goto 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: és default: 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 szakaszai switch 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 casevagy goto default utasításban végződik, de az utasításlista végpontját elérhetetlenné tevő szerkezetek engedélyezettek. A logikai kifejezés while által szabályozott utasítás például true ismert, hogy soha nem éri el a végpontját. Hasonlóképpen, egy throw vagy return 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ípus string. 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 a case címkeállandóval. végjegyzet Ha egy switch utasítás string típusa referencia típus vagy nullálható érték típus, akkor az érték null engedélyezett case 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 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 vagy goto 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 a switch 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 egy continue utasítás végrehajtásából), a vezérlő átkerül az while utasítás elejére.
  • Ha a logikai kifejezés hozamot eredményez false, a vezérlőelem átkerül az while 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 a while utasításból.
  • Az while utasítás elérhető, és a logikai kifejezés nem rendelkezik állandó értékkel true.

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 ad true, a vezérlőelem átkerül az do utasítás elejére. Ellenkező esetben a vezérlőelem átkerül az utasítás végpontjára do .

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 a do 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 egy continue 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 az for 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ékkel false.

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 a for utasításból.
  • Az for utasítás elérhető, és egy for_condition van jelen, és nem rendelkezik állandó értékkel true.

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 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 nullvan, a rendszer futásidőben ad ki egy System.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 a IEnumerable interfészre (mivel System.Array implementálja ezt az interfészt). A gyűjtemény típusa az IEnumerable interfész, az enumerátor típusa az IEnumerator interfész, az iteráció típusa pedig a tömbtípus elemtípusa X.
  • Ha a X típusa , akkor van egy implicit átalakítás a dynamic a interfészre (IEnumerable). A gyűjtemény típusa az IEnumerable interfész, az enumerátor típusa pedig az IEnumerator interfész. Ha a var azonosítót local_variable_typeként adjuk meg, akkor az iteráció típusa dynamic; 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óval GetEnumerator é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ípusa GetEnumerator 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óval Current é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óval MoveNext é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ípusa E, az iteráció típusa pedig a Current tulajdonság típusa. A Current tulajdonság tartalmazhatja a ref módosítót, ebben az esetben a visszaadott kifejezés egy variable_reference (§9.5), amely opcionálisan írásvédett.
  • Ellenkező esetben ellenőrizze, hogy van-e felsorolható felület:
    • Ha az összes típus Tᵢ között, amelynek implicit konverziója XIEnumerable<Tᵢ> van, van egy egyedi típusT, amely T nemdynamic, és az összes többi Tᵢ 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ípusa T.
    • Ellenkező esetben, ha egynél több ilyen típus Tvan, 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 az object.
    • Ellenkező esetben hibaüzenet jelenik meg, és nem történik további lépés.

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 Tvagy 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 a while cikluson kívül deklarálnák, akkor az minden iteráció között meg lesz osztva, és a for ciklus utáni értéke a végső érték lenne, 13amit a hívása f nyomtatna ki. Ehelyett, mivel minden iteráció saját változóval vrendelkezik, az első iterációban rögzített f érték továbbra is az értéket 7fogja tárolni, ami a nyomtatni fog. (Vegye figyelembe, hogy a C# korábbi verziói a v cikluson kívül deklaráltak while.)

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 a System.IDisposable felületre, akkor

    • Ha E nem null értékű, akkor a finally 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, akkor e konvertálása System.IDisposable-vé nem okozhat boxolást.

  • Ellenkező esetben, ha E lezárt típus, a finally 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 – 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.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, hogy int, ami numbers 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óval GetAsyncEnumerator é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ípusa GetAsyncEnumerator 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óval Current é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óval MoveNextAsync é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ípusa E, az iteráció típusa pedig a Current tulajdonság típusa.
  • 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ója XIAsyncEnumerable<Tᵢ> van, van egy egyedi típusT, amely T nemdynamic, és az összes többi Tᵢ 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ípusa T.
    • Ellenkező esetben, ha egynél több ilyen típus Tvan, 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 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ított try 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, 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 break utasítás kilép egy vagy több try blokkból a társított finally blokkokkal, a vezérlő kezdetben a finally legbelső try utasítás blokkjába kerül. 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.
  • 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, 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 continue utasítás kilép egy vagy több try blokkból a társított finally blokkokkal, a vezérlő kezdetben a finally legbelső try utasítás blokkjába kerül. 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.
  • 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 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 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ában

class 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öbb try blokkból a társított finally blokkokkal, a vezérlő kezdetben a finally legbelső try utasítás blokkjába kerül. 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.
  • 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öbb try vagy catch blokk tartalmazza, amelyek társított finally blokkokkal rendelkeznek, a vezérlés kezdetben a legbelső finally utasítás try blokkjába kerül. 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 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.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 (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ás Seseté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 blokk S befogja a dobópontot, és S egy vagy több catch záradékkal rendelkezik, a catch 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ípust T határoz meg (vagy egy olyan típusparamétert, amely futásidőben kivételtípust Tjelöl), és amelyből E a futtatási idő típusa T 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 egy catch záradék kivételszűrőt tartalmaz, akkor a catch záradék egyezésnek minősül, ha a kivételszűrő kiértékeli a következőt true: . Az általános catch (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 egy catch blokk S a dobópontot zárja be, és ha S van blokkja finally , akkor a vezérlő átkerül a finally blokkba. Ha a finally 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 a finally blokk végpontját, az aktuális kivétel feldolgozása folytatódik.
  • 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ános catch záradék használható. Az általános catch záradék tehát szemantikailag eltér a típust System.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 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 helyett e 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, 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 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 a finally záradék Method 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 egy catch blokk végpontja elérhető.
  • Ha egy finally blokk jelen van, a blokk végpontja finally 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.IDisposableaszinkron IAsyncDisposable streamek) felületét, amely egyetlen paraméter nélküli metódust Dispose tartalmaz (DisposeAsyncaszinkron 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 resourceSystem.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 az TextReader osztályok implementálják az IDisposable 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' ';'
    ;

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 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 egy finally záradékában egy try utasításnak.
  • Fordítási idejű hiba, ha egy yield return utasítás bárhol megjelenik egy try 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öbb try blokkon belül van, a társított finally blokkok végrehajtása jelenleg nem történik meg.
  • Az MoveNext enumerátor objektum metódusa visszatér true 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öbb try , társított finally blokkot tartalmazó blokk zárja be, 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 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 vagy Dispose 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.