Bagikan melalui


13 Pernyataan

13.1 Umum

C# menyediakan berbagai pernyataan.

Catatan: Sebagian besar pernyataan ini akan akrab bagi pengembang yang telah diprogram di C dan C++. catatan akhir

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) dan fixed_statement (§23.7) hanya tersedia dalam kode yang tidak aman (§23).

Nonterminal embedded_statement digunakan untuk pernyataan yang muncul dalam pernyataan lain. Penggunaan embedded_statement daripada pernyataan mengecualikan penggunaan pernyataan deklarasi dan pernyataan berlabel dalam konteks ini.

Contoh: Kode

void F(bool b)
{
   if (b)
      int i = 44;
}

menghasilkan kesalahan waktu kompilasi karena if pernyataan memerlukan embedded_statement daripada pernyataan untuk cabangnya if . Jika kode ini diizinkan, maka variabel i akan dideklarasikan, tetapi tidak pernah dapat digunakan. Namun, perhatikan bahwa dengan menempatkan ideklarasi dalam blok, contohnya valid.

contoh akhir

13.2 Titik akhir dan keterjangkauan

Setiap pernyataan memiliki titik akhir. Dalam istilah intuitif, titik akhir pernyataan adalah lokasi yang segera mengikuti pernyataan. Aturan eksekusi untuk pernyataan komposit (pernyataan yang berisi pernyataan yang disematkan) menentukan tindakan yang diambil saat kontrol mencapai titik akhir pernyataan yang disematkan.

Contoh: Ketika kontrol mencapai titik akhir pernyataan dalam blok, kontrol ditransfer ke pernyataan berikutnya di blok. contoh akhir

Jika pernyataan mungkin dapat dicapai dengan eksekusi, pernyataan tersebut dikatakan dapat dijangkau. Sebaliknya, jika tidak ada kemungkinan bahwa pernyataan akan dijalankan, pernyataan tersebut dikatakan tidak dapat dijangkau.

Contoh: Dalam kode berikut

void F()
{
    Console.WriteLine("reachable");
    goto Label;
    Console.WriteLine("unreachable");
  Label:
    Console.WriteLine("reachable");
}

pemanggilan konsol.WriteLine kedua tidak dapat dijangkau karena tidak ada kemungkinan bahwa pernyataan akan dijalankan.

contoh akhir

Peringatan dilaporkan jika pernyataan selain throw_statement, blokir, atau empty_statement tidak dapat dijangkau. Ini secara khusus bukan kesalahan agar pernyataan tidak dapat dijangkau.

Catatan: Untuk menentukan apakah pernyataan atau titik akhir tertentu dapat dijangkau, pengkompilasi melakukan analisis alur sesuai dengan aturan keterjangkauan yang ditentukan untuk setiap pernyataan. Analisis alur memperhitungkan nilai ekspresi konstanta (§12,23) yang mengontrol perilaku pernyataan, tetapi kemungkinan nilai ekspresi non-konstan tidak dipertimbangkan. Dengan kata lain, untuk tujuan analisis alur kontrol, ekspresi non-konstan dari jenis tertentu dianggap memiliki nilai yang mungkin dari jenis tersebut.

Dalam contoh

void F()
{
    const int i = 1;
    if (i == 2)
        Console.WriteLine("unreachable");
}

ekspresi Boolean dari if pernyataan adalah ekspresi konstan == karena kedua operan operator adalah konstanta. Karena ekspresi konstan dievaluasi pada waktu kompilasi, menghasilkan nilai false, Console.WriteLine pemanggilan dianggap tidak dapat dijangkau. Namun, jika i diubah menjadi variabel lokal

void F()
{
    int i = 1;
    if (i == 2)
        Console.WriteLine("reachable");
}

pemanggilan Console.WriteLine dianggap dapat dijangkau, meskipun, pada kenyataannya, itu tidak akan pernah dijalankan.

catatan akhir

Blok anggota fungsi atau fungsi anonim selalu dianggap dapat dijangkau. Dengan secara berturut-turut mengevaluasi aturan keterjangkauan setiap pernyataan dalam blok, keterjangkauan pernyataan tertentu dapat ditentukan.

Contoh: Dalam kode berikut

void F(int x)
{
    Console.WriteLine("start");
    if (x < 0)
        Console.WriteLine("negative");
}

keterjangkauan yang kedua Console.WriteLine ditentukan sebagai berikut:

  • Pernyataan ekspresi pertama Console.WriteLine dapat dijangkau karena blok F metode dapat dijangkau (§13.3).
  • Titik akhir pernyataan ekspresi pertama Console.WriteLine dapat dijangkau karena pernyataan tersebut dapat dijangkau (§13,7 dan §13,3).
  • Pernyataan if dapat dijangkau karena titik akhir pernyataan ekspresi pertama Console.WriteLine dapat dijangkau (§13,7 dan §13,3).
  • Pernyataan ekspresi kedua Console.WriteLine dapat dijangkau karena ekspresi Boolean dari if pernyataan tidak memiliki nilai falsekonstanta .

contoh akhir

Ada dua situasi di mana itu adalah kesalahan waktu kompilasi agar titik akhir pernyataan dapat dijangkau:

  • switch Karena pernyataan tidak mengizinkan bagian pengalihan untuk "jatuh melalui" ke bagian sakelar berikutnya, itu adalah kesalahan waktu kompilasi untuk titik akhir daftar pernyataan bagian sakelar agar dapat dijangkau. Jika kesalahan ini terjadi, biasanya merupakan indikasi bahwa break pernyataan hilang.

  • Ini adalah kesalahan waktu kompilasi untuk titik akhir blok anggota fungsi atau fungsi anonim yang menghitung nilai yang dapat dijangkau. Jika kesalahan ini terjadi, biasanya merupakan indikasi bahwa return pernyataan hilang (§13.10.5).

13.3 Blok

13.3.1 Umum

Blok mengizinkan beberapa pernyataan ditulis dalam konteks di mana satu pernyataan diizinkan.

block
    : '{' statement_list? '}'
    ;

Blok terdiri dari statement_list opsional (§13.3.2), diapit kurung kurawal. Jika daftar pernyataan dihilangkan, blok tersebut dikatakan kosong.

Blok mungkin berisi pernyataan deklarasi (§13.6). Cakupan variabel lokal atau konstanta yang dideklarasikan dalam blok adalah blok .

Blok dijalankan sebagai berikut:

  • Jika blok kosong, kontrol ditransfer ke titik akhir blok.
  • Jika blok tidak kosong, kontrol akan ditransfer ke daftar pernyataan. Kapan dan jika kontrol mencapai titik akhir daftar pernyataan, kontrol ditransfer ke titik akhir blok.

Daftar pernyataan blok dapat dijangkau jika blok itu sendiri dapat dijangkau.

Titik akhir blok dapat dijangkau jika blok kosong atau jika titik akhir daftar pernyataan dapat dijangkau.

Blok yang berisi satu atau beberapa yield pernyataan (§13.15) disebut blok iterator. Blok iterator digunakan untuk mengimplementasikan anggota fungsi sebagai iterator (§15,15). Beberapa batasan tambahan berlaku untuk blok iterator:

  • Ini adalah kesalahan waktu kompilasi agar return pernyataan muncul di blok iterator (tetapi yield return pernyataan diizinkan).
  • Ini adalah kesalahan waktu kompilasi untuk blok iterator untuk berisi konteks yang tidak aman (§23.2). Blok iterator selalu mendefinisikan konteks yang aman, bahkan ketika deklarasinya disarangkan dalam konteks yang tidak aman.

13.3.2 Daftar pernyataan

Daftar pernyataan terdiri dari satu atau beberapa pernyataan yang ditulis secara berurutan. Daftar pernyataan terjadi dalam blok (§13,3) dan dalam switch_blocks (§13.8.3).

statement_list
    : statement+
    ;

Daftar pernyataan dijalankan dengan mentransfer kontrol ke pernyataan pertama. Kapan dan jika kontrol mencapai titik akhir pernyataan, kontrol ditransfer ke pernyataan berikutnya. Kapan dan jika kontrol mencapai titik akhir pernyataan terakhir, kontrol ditransfer ke titik akhir daftar pernyataan.

Pernyataan dalam daftar pernyataan dapat dijangkau jika setidaknya salah satu hal berikut ini benar:

  • Pernyataan tersebut adalah pernyataan pertama dan daftar pernyataan itu sendiri dapat dijangkau.
  • Titik akhir pernyataan sebelumnya dapat dijangkau.
  • Pernyataan tersebut adalah pernyataan berlabel dan label direferensikan oleh pernyataan yang dapat dijangkau goto .

Titik akhir daftar pernyataan dapat dijangkau jika titik akhir pernyataan terakhir dalam daftar dapat dijangkau.

13.4 Pernyataan kosong

Empty_statement tidak melakukan apa-apa.

empty_statement
    : ';'
    ;

Pernyataan kosong digunakan ketika tidak ada operasi untuk dilakukan dalam konteks di mana pernyataan diperlukan.

Eksekusi pernyataan kosong hanya mentransfer kontrol ke titik akhir pernyataan. Dengan demikian, titik akhir pernyataan kosong dapat dijangkau jika pernyataan kosong dapat dijangkau.

Contoh: Pernyataan kosong dapat digunakan saat menulis while pernyataan dengan isi null:

bool ProcessMessage() {...}
void ProcessMessages()
{
    while (ProcessMessage())
        ;
}

Selain itu, pernyataan kosong dapat digunakan untuk mendeklarasikan label tepat sebelum penutupan "}" blok:

void F(bool done)
{
    ...
    if (done)
    {
        goto exit;
    }
    ...
  exit:
    ;
}

contoh akhir

13.5 Pernyataan berlabel

labeled_statement mengizinkan pernyataan diawali dengan label. Pernyataan berlabel diizinkan dalam blok, tetapi tidak diizinkan sebagai pernyataan yang disematkan.

labeled_statement
    : identifier ':' statement
    ;

Pernyataan berlabel mendeklarasikan label dengan nama yang diberikan oleh pengidentifikasi. Cakupan label adalah seluruh blok tempat label dideklarasikan, termasuk blok berlapis apa pun. Ini adalah kesalahan waktu kompilasi untuk dua label dengan nama yang sama untuk memiliki cakupan yang tumpang tindih.

Label dapat dirujuk dari goto pernyataan (§13.10.4) dalam cakupan label.

Catatan: Ini berarti bahwa goto pernyataan dapat mentransfer kontrol dalam blok dan di luar blok, tetapi tidak pernah menjadi blok. catatan akhir

Label memiliki ruang deklarasi sendiri dan tidak mengganggu pengidentifikasi lainnya.

Contoh: Contoh

int F(int x)
{
    if (x >= 0)
    {
        goto x;
    }
    x = -x;
  x:
    return x;
}

valid dan menggunakan nama x sebagai parameter dan label.

contoh akhir

Eksekusi pernyataan berlabel persis sesuai dengan eksekusi pernyataan setelah label.

Selain keterjangkauan yang disediakan oleh aliran kontrol normal, pernyataan berlabel dapat dijangkau jika label dirujuk oleh pernyataan yang dapat goto dijangkau, kecuali goto pernyataan berada di dalam try blok atau catch blok try_statement yang mencakup finally blok yang titik akhirnya tidak dapat dijangkau, dan pernyataan berlabel berada di luar try_statement.

13.6 Pernyataan deklarasi

13.6.1 Umum

declaration_statement mendeklarasikan satu atau beberapa variabel lokal, satu atau beberapa konstanta lokal, atau fungsi lokal. Pernyataan deklarasi diizinkan dalam blok dan blok sakelar, tetapi tidak diizinkan sebagai pernyataan yang disematkan.

declaration_statement
    : local_variable_declaration ';'
    | local_constant_declaration ';'
    | local_function_declaration
    ;

Variabel lokal dinyatakan menggunakan local_variable_declaration (§13.6.2). Konstanta lokal dinyatakan menggunakan local_constant_declaration (§13.6.3). Fungsi lokal dinyatakan menggunakan local_function_declaration (§13.6.4).

Nama yang dideklarasikan dimasukkan ke dalam ruang deklarasi penutup terdekat (§7,3).

13.6.2 Deklarasi variabel lokal

13.6.2.1 Umum

local_variable_declaration mendeklarasikan satu atau beberapa variabel lokal.

local_variable_declaration
    : implicitly_typed_local_variable_declaration
    | explicitly_typed_local_variable_declaration
    | explicitly_typed_ref_local_variable_declaration
    ;

Deklarasi yang diketik secara implisit berisi kata kunci kontekstual (§6.4.4) var menghasilkan ambiguitas sinaks antara tiga kategori yang diselesaikan sebagai berikut:

  • Jika tidak ada jenis bernama var dalam cakupan dan input cocok dengan implicitly_typed_local_variable_declaration maka dipilih;
  • Jika tidak, jika jenis bernama var berada dalam cakupan, implicitly_typed_local_variable_declaration tidak dianggap sebagai kecocokan yang mungkin.

Dalam local_variable_declaration setiap variabel diperkenalkan oleh deklarator, yang merupakan salah satu variabel lokal implicitly_typed_local_variable_declarator, explicitly_typed_local_variable_declarator atau ref_local_variable_declarator untuk masing-masing variabel lokal yang diketik secara implisit, diketik secara eksplisit, dan ref. Deklarator mendefinisikan nama (pengidentifikasi) dan nilai awal, jika ada, dari variabel yang diperkenalkan.

Jika ada beberapa deklarator dalam deklarasi maka mereka diproses, termasuk ekspresi inisialisasi apa pun, untuk kiri ke kanan (§9.4.4.5).

Catatan: Untuk local_variable_declaration yang tidak terjadi sebagai for_initializer (§13.9.4) atau resource_acquisition (§13.14) urutan kiri ke kanan ini setara dengan setiap deklarator yang berada dalam local_variable_declaration terpisah. Contohnya:

void F()
{
    int x = 1, y, z = x * 2;
}

setara dengan:

void F()
{
    int x = 1;
    int y;
    int z = x * 2;
}

catatan akhir

Nilai variabel lokal diperoleh dalam ekspresi menggunakan simple_name (§12.8.4). Variabel lokal pasti akan ditetapkan (§9,4) di setiap lokasi tempat nilainya diperoleh. Setiap variabel lokal yang diperkenalkan oleh local_variable_declaration awalnya tidak ditetapkan (§9.4.3). Jika deklarator memiliki ekspresi inisialisasi, variabel lokal yang diperkenalkan diklasifikasikan seperti yang ditetapkan di akhir deklarator (§9.4.4.5).

Cakupan variabel lokal yang diperkenalkan oleh local_variable_declaration didefinisikan sebagai berikut (§7.7):

  • Jika deklarasi terjadi sebagai for_initializer maka cakupannya adalah for_initializer, for_condition, for_iterator, dan embedded_statement (§13.9.4);
  • Jika deklarasi terjadi sebagai resource_acquisition maka cakupan adalah blok terluar dari ekspansi using_statement yang setara secara semantik (§13,14);
  • Jika tidak, cakupan adalah blok tempat deklarasi terjadi.

Ini adalah kesalahan untuk merujuk ke variabel lokal berdasarkan nama dalam posisi tekstual yang mendahului deklaratornya, atau dalam ekspresi inisialisasi apa pun dalam deklaratornya. Dalam cakupan variabel lokal, ini adalah kesalahan waktu kompilasi untuk mendeklarasikan variabel lokal lain, fungsi lokal, atau konstanta dengan nama yang sama.

Konteks ref-safe (§9,7.2) dari variabel lokal ref adalah konteks ref-safe dari variable_reference inisialisasinya. Konteks ref-safe dari variabel lokal non-ref adalah blok deklarasi.

13.6.2.2 Deklarasi variabel lokal yang diketik secara implisit

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
    ;

Implicitly_typed_local_variable_declaration memperkenalkan satu variabel lokal, pengidentifikasi. Ekspresi atau variable_reference harus memiliki jenis waktu kompilasi, T. Alternatif pertama mendeklarasikan variabel dengan nilai awal ekspresi; jenisnya adalah T? ketika T adalah jenis referensi yang tidak dapat diubah ke null, jika tidak, jenisnya adalah T. Alternatif kedua mendeklarasikan variabel ref dengan nilai refawal variable_reference; jenisnya adalah ref T? ketika T adalah jenis referensi yang tidak dapat diubah ke null, jika tidak, jenisnya adalah ref T. (ref_kind dijelaskan dalam §15.6.1.)

Contoh:

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;

Deklarasi variabel lokal yang diketik secara implisit di atas justru setara dengan deklarasi yang diketik secara eksplisit berikut:

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;

Berikut ini adalah deklarasi variabel lokal yang salah yang diketik secara implisit:

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

contoh akhir

13.6.2.3 Deklarasi variabel lokal yang diketik secara eksplisit

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
    ;

Explicity_typed_local_variable_declaration memperkenalkan satu atau beberapa variabel lokal dengan jenis yang ditentukan.

Jika ada local_variable_initializer maka jenisnya harus sesuai dengan aturan penetapan sederhana (§12.21.2) atau inisialisasi array (§17,7) dan nilainya ditetapkan sebagai nilai awal variabel.

13.6.2.4 Deklarasi variabel lokal ref yang diketik secara eksplisit

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
    ;

variable_reference inisialisasi harus memiliki jenis jenis dan memenuhi persyaratan yang sama seperti untuk penugasan ref (§12.21.3).

Jika ref_kind adalah ref readonly, pengidentifikasi yang dideklarasikan adalah referensi ke variabel yang diperlakukan sebagai baca-saja. Jika tidak, jika ref_kind adalah ref, pengidentifikasiyang dinyatakan adalah referensi ke variabel yang akan dapat ditulis.

Ini adalah kesalahan saat waktu kompilasi untuk mendeklarasikan variabel lokal ref, atau variabel tipe ref struct, dalam metode yang dideklarasikan dengan method_modifierasync, atau dalam iterator (§15.15).

13.6.3 Deklarasi konstanta lokal

local_constant_declaration mendeklarasikan satu atau beberapa konstanta lokal.

local_constant_declaration
    : 'const' type constant_declarators
    ;

constant_declarators
    : constant_declarator (',' constant_declarator)*
    ;

constant_declarator
    : identifier '=' constant_expression
    ;

Jenislocal_constant_declaration menentukan jenis konstanta yang diperkenalkan oleh deklarasi. Jenisnya diikuti oleh daftar constant_declarator, yang masing-masing memperkenalkan konstanta baru. constant_declarator terdiri dari pengidentifikasi yang memberi nama konstanta, diikuti dengan token ""=, diikuti oleh constant_expression (§12,23) yang memberikan nilai konstanta.

Jenis dan constant_expression deklarasi konstanta lokal harus mengikuti aturan yang sama dengan deklarasi anggota konstanta (§15.4).

Nilai konstanta lokal diperoleh dalam ekspresi menggunakan simple_name (§12.8.4).

Cakupan konstanta lokal adalah blok tempat deklarasi terjadi. Ini adalah kesalahan untuk merujuk ke konstanta lokal dalam posisi tekstual yang mendahului akhir constant_declarator.

Deklarasi konstanta lokal yang menyatakan beberapa konstanta setara dengan beberapa deklarasi konstanta tunggal dengan jenis yang sama.

13.6.4 Deklarasi fungsi lokal

local_function_declaration mendeklarasikan fungsi lokal.

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

Catatan tata bahasa: Saat mengenali local_function_body jika alternatif null_conditional_invocation_expression dan ekspresi berlaku maka yang pertama akan dipilih. (§15.6.1)

Contoh: Ada dua kasus penggunaan umum untuk fungsi lokal: metode iterator dan metode asinkron. Dalam metode iterator, pengecualian apa pun diamati hanya ketika memanggil kode yang menghitung urutan yang dikembalikan. Dalam metode asinkron, pengecualian apa pun hanya diamati ketika Tugas yang dikembalikan ditunggu. Contoh berikut menunjukkan pemisahan validasi parameter dari implementasi iterator menggunakan fungsi lokal:

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

contoh akhir

Kecuali ditentukan sebaliknya di bawah ini, semantik semua elemen tata bahasa sama dengan untuk method_declaration (§15.6.1), baca dalam konteks fungsi lokal alih-alih metode.

Pengidentifikasi local_function_declaration harus unik dalam cakupan blok yang dinyatakan, termasuk ruang deklarasi variabel lokal yang mencakup. Salah satu konsekuensi dari ini adalah bahwa local_function_declarationyang kelebihan beban tidak diizinkan.

Local_function_declaration dapat mencakup satu async pengubah (§15,14) dan satu unsafe pengubah (§23,1). Jika deklarasi mencakup pengubah async , maka jenis pengembaliannya adalah void atau «TaskType» jenis (§15.14.1). Jika deklarasi menyertakan pengubah static , fungsinya adalah fungsi lokal statis; jika tidak, itu adalah fungsi lokal non-statis. Ini adalah kesalahan waktu kompilasi untuk type_parameter_list atau parameter_list untuk berisi atribut. Jika fungsi lokal dinyatakan dalam konteks tidak aman (§23.2), fungsi lokal dapat menyertakan kode yang tidak aman, bahkan jika deklarasi fungsi lokal tidak menyertakan pengubah unsafe .

Fungsi lokal dideklarasikan pada cakupan blok. Fungsi lokal non-statis dapat mengambil variabel dari cakupan penutup sementara fungsi lokal statis tidak boleh (sehingga tidak memiliki akses ke lokal, parameter, fungsi lokal non-statis, atau this). Ini adalah kesalahan waktu kompilasi jika variabel yang ditangkap dibaca oleh isi fungsi lokal non-statis tetapi tidak pasti ditetapkan sebelum setiap panggilan ke fungsi. Kompilator harus menentukan variabel mana yang pasti ditetapkan saat pengembalian (§9.4.4.33).

Ketika jenis this adalah jenis struct, itu adalah kesalahan waktu kompilasi bagi isi fungsi lokal untuk mengakses this. Ini benar apakah akses eksplisit (seperti dalam this.x) atau implisit (seperti di x mana x adalah anggota instans struct). Aturan ini hanya melarang akses tersebut dan tidak memengaruhi apakah pencarian anggota menghasilkan anggota struktur.

Ini adalah kesalahan waktu kompilasi bagi isi fungsi lokal untuk berisi goto pernyataan, break pernyataan, atau continue pernyataan yang targetnya berada di luar isi fungsi lokal.

Catatan: aturan di atas untuk this dan goto mencerminkan aturan untuk fungsi anonim dalam §12.19.3. catatan akhir

Fungsi lokal dapat dipanggil dari titik leksikal sebelum deklarasinya. Namun, ini adalah kesalahan waktu kompilasi agar fungsi dideklarasikan secara leksikal sebelum deklarasi variabel yang digunakan dalam fungsi lokal (§7,7).

Ini adalah kesalahan waktu kompilasi bagi fungsi lokal untuk mendeklarasikan parameter, parameter jenis, atau variabel lokal dengan nama yang sama dengan yang dideklarasikan dalam ruang deklarasi variabel lokal yang mencakup.

Badan fungsi lokal selalu dapat dijangkau. Titik akhir deklarasi fungsi lokal dapat dijangkau jika titik awal deklarasi fungsi lokal dapat dijangkau.

Contoh: Dalam contoh berikut, isi L dapat dijangkau meskipun titik L awal tidak dapat dijangkau. Karena titik L awal tidak dapat dijangkau, pernyataan setelah titik L akhir tidak dapat dijangkau:

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

Dengan kata lain, lokasi deklarasi fungsi lokal tidak memengaruhi keterjangkauan pernyataan apa pun dalam fungsi yang berisi. contoh akhir

Jika jenis argumen ke fungsi lokal adalah dynamic, fungsi yang akan dipanggil harus diselesaikan pada waktu kompilasi, bukan runtime.

Fungsi lokal tidak boleh digunakan dalam pohon ekspresi.

Fungsi lokal statis

  • Dapat mereferensikan anggota statis, parameter jenis, definisi konstanta, dan fungsi lokal statis dari cakupan penutup.
  • Tidak boleh mereferensikan this atau base atau anggota instans dari referensi implisit this , atau variabel lokal, parameter, atau fungsi lokal non-statis dari cakupan penutup. Namun, semua ini diizinkan dalam nameof() ekspresi.

13.7 Pernyataan ekspresi

Expression_statement mengevaluasi ekspresi tertentu. Nilai yang dihitung oleh ekspresi, jika ada, dibuang.

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
    ;

Tidak semua ekspresi diizinkan sebagai pernyataan.

Catatan: Secara khusus, ekspresi seperti x + y dan x == 1, yang hanya menghitung nilai (yang akan dibuang), tidak diizinkan sebagai pernyataan. catatan akhir

Eksekusi expression_statement mengevaluasi ekspresi yang terkandung lalu mentransfer kontrol ke titik akhir expression_statement. Titik akhir expression_statement dapat dijangkau jika expression_statement tersebut dapat dijangkau.

13.8 Pernyataan pemilihan

13.8.1 Umum

Pernyataan pilihan memilih salah satu dari sejumlah pernyataan yang mungkin untuk eksekusi berdasarkan nilai beberapa ekspresi.

selection_statement
    : if_statement
    | switch_statement
    ;

13.8.2 Pernyataan if

Pernyataan if memilih pernyataan untuk eksekusi berdasarkan nilai ekspresi Boolean.

if_statement
    : 'if' '(' boolean_expression ')' embedded_statement
    | 'if' '(' boolean_expression ')' embedded_statement
      'else' embedded_statement
    ;

Bagian else dikaitkan dengan pendahuluan if terdekat secara leksikal yang diizinkan oleh sintaks.

Contoh: Dengan demikian, pernyataan if formulir

if (x) if (y) F(); else G();

setara dengan:

if (x)
{
    if (y)
    {
        F();
    }
    else
    {
        G();
    }
}

contoh akhir

Pernyataan if dijalankan sebagai berikut:

  • boolean_expression (§12.24) dievaluasi.
  • Jika ekspresi Boolean menghasilkan true, kontrol ditransfer ke pernyataan pertama yang disematkan. Kapan dan jika kontrol mencapai titik akhir pernyataan tersebut, kontrol ditransfer ke titik if akhir pernyataan.
  • Jika ekspresi Boolean menghasilkan false dan jika ada else bagian, kontrol ditransfer ke pernyataan tersemat kedua. Kapan dan jika kontrol mencapai titik akhir pernyataan tersebut, kontrol ditransfer ke titik if akhir pernyataan.
  • Jika ekspresi Boolean menghasilkan false dan jika bagian else tidak ada, kontrol ditransfer ke titik if akhir pernyataan.

Pernyataan pertama yang disematkan dari pernyataan if dapat dijangkau jika if pernyataan dapat dijangkau dan ekspresi Boolean tidak memiliki nilai falsekonstanta .

Pernyataan pernyataan kedua yang if disematkan, jika ada, dapat dijangkau jika if pernyataan dapat dijangkau dan ekspresi Boolean tidak memiliki nilai truekonstanta .

Titik if akhir pernyataan dapat dijangkau jika titik akhir setidaknya salah satu pernyataan yang disematkan dapat dijangkau. Selain itu, titik if akhir pernyataan tanpa else bagian tidak dapat dijangkau jika if pernyataan dapat dijangkau dan ekspresi Boolean tidak memiliki nilai truekonstanta .

13.8.3 Pernyataan pengalihan

Pernyataan switch memilih untuk eksekusi daftar pernyataan yang memiliki label sakelar terkait yang sesuai dengan nilai ekspresi pengalihan.

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
    ;

switch_statement terdiri dari kata kunci switch, diikuti oleh ekspresi yang dikurung (disebut ekspresi pengalihan), diikuti oleh switch_block. switch_block terdiri dari nol atau lebih switch_section, diapit kurung kurawal. Setiap switch_section terdiri dari satu atau beberapa switch_labeldiikuti oleh statement_list (§13.3.2). Setiap switch_label yang berisi case memiliki pola terkait (§11) di mana nilai ekspresi pengalihan diuji. Jika case_guard ada, ekspresinya akan secara implisit dapat dikonversi ke jenis bool dan ekspresi tersebut dievaluasi sebagai kondisi tambahan agar kasus dianggap puas.

Jenis pernyataan yang switch mengatur ditetapkan oleh ekspresi pengalihan.

  • Jika jenis ekspresi pengalihan adalah sbyte, , byte, short, ushortintuintlongulong, char, bool, string, , atau enum_type, atau jika itu adalah jenis nilai nullable yang sesuai dengan salah satu jenis ini, maka itu adalah jenis switch pernyataan yang mengatur.
  • Jika tidak, jika persis satu konversi implisit yang ditentukan pengguna ada dari jenis ekspresi pengalihan ke salah satu kemungkinan jenis tata kelola berikut: sbyte, , byte, shortushortintuintlongulongchar, stringatau, jenis nilai nullable yang sesuai dengan salah satu jenis tersebut, maka jenis yang dikonversi adalah jenis switch pernyataan yang mengatur.
  • Jika tidak, jenis pernyataan yang switch mengatur adalah jenis ekspresi pengalihan. Ini adalah kesalahan jika tidak ada jenis seperti itu.

Mungkin ada paling banyak satu default label dalam pernyataan switch .

Ini adalah kesalahan jika pola label sakelar apa pun tidak berlaku (§11.2.1) ke jenis ekspresi input.

Ini adalah kesalahan jika pola label sakelar apa pun disubsumsikan oleh (§11.3) kumpulan pola label switch sebelumnya dari pernyataan pengalihan yang tidak memiliki penjaga kasus atau yang penjaga kasusnya adalah ekspresi konstan dengan nilai benar.

Contoh:

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

contoh akhir

Pernyataan switch dijalankan sebagai berikut:

  • Ekspresi pengalihan dievaluasi dan dikonversi ke jenis yang mengatur.
  • Kontrol ditransfer sesuai dengan nilai ekspresi sakelar yang dikonversi:
    • Pola pertama leksikal dalam kumpulan case label dalam pernyataan yang sama switch yang cocok dengan nilai ekspresi pengalihan, dan di mana ekspresi penjaga tidak ada atau dievaluasi ke true, menyebabkan kontrol ditransfer ke daftar pernyataan mengikuti label yang cocok case .
    • Jika tidak, jika default label ada, kontrol akan ditransfer ke daftar pernyataan setelah default label.
    • Jika tidak, kontrol ditransfer ke titik switch akhir pernyataan.

Catatan: Urutan pola yang dicocokkan pada runtime tidak ditentukan. Pengkompilasi diizinkan (tetapi tidak diperlukan) untuk mencocokkan pola secara tidak berurutan, dan untuk menggunakan kembali hasil pola yang sudah cocok untuk menghitung hasil pencocokan pola lain. Namun demikian, kompilator diperlukan untuk menentukan pola pertama secara leksikal yang cocok dengan ekspresi dan yang klausul penjaganya tidak ada atau dievaluasi menjadi true. catatan akhir

Jika titik akhir daftar pernyataan bagian sakelar dapat dijangkau, kesalahan waktu kompilasi terjadi. Ini dikenal sebagai aturan "tidak jatuh".

Contoh: Contoh

switch (i)
{
    case 0:
        CaseZero();
        break;
    case 1:
        CaseOne();
        break;
    default:
        CaseOthers();
        break;
}

valid karena tidak ada bagian sakelar yang memiliki titik akhir yang dapat dijangkau. Tidak seperti C dan C++, eksekusi bagian sakelar tidak diizinkan untuk "jatuh" ke bagian sakelar berikutnya, dan contohnya

switch (i)
{
    case 0:
        CaseZero();
    case 1:
        CaseZeroOrOne();
    default:
        CaseAny();
}

menghasilkan kesalahan waktu kompilasi. Ketika eksekusi bagian switch akan diikuti dengan eksekusi bagian switch lain, eksplisit goto case atau goto default pernyataan akan digunakan:

switch (i)
{
    case 0:
        CaseZero();
        goto case 1;
    case 1:
        CaseZeroOrOne();
        goto default;
    default:
        CaseAny();
        break;
}

contoh akhir

Beberapa label diizinkan dalam switch_section.

Contoh: Contoh

switch (i)
{
    case 0:
        CaseZero();
        break;
    case 1:
        CaseOne();
        break;
    case 2:
    default:
        CaseTwo();
        break;
}

valid. Contoh tidak melanggar aturan "no fall through" karena label case 2: dan default: merupakan bagian dari switch_section yang sama.

contoh akhir

Catatan: Aturan "no fall through" mencegah kelas umum bug yang terjadi di C dan C++ ketika break pernyataan dihilangkan secara tidak sengaja. Misalnya, bagian pernyataan di switch atas dapat dibalik tanpa memengaruhi perilaku pernyataan:

switch (i)
{
    default:
        CaseAny();
        break;
    case 1:
        CaseZeroOrOne();
        goto default;
    case 0:
        CaseZero();
        goto case 1;
}

catatan akhir

Catatan: Daftar pernyataan bagian pengalihan biasanya berakhiran breakpernyataan , , goto caseatau goto default , tetapi konstruksi apa pun yang merender titik akhir daftar pernyataan yang tidak dapat dijangkau diizinkan. Misalnya, pernyataan yang while dikendalikan oleh ekspresi true Boolean diketahui tidak pernah mencapai titik akhirnya. Demikian juga, pernyataan throw atau return selalu mentransfer kontrol di tempat lain dan tidak pernah mencapai titik akhirnya. Dengan demikian, contoh berikut valid:

switch (i)
{
     case 0:
         while (true)
         {
             F();
         }
     case 1:
         throw new ArgumentException();
     case 2:
         return;
}

catatan akhir

Contoh: Jenis pernyataan yang switch mengatur dapat berupa jenis string. Contohnya:

void DoCommand(string command)
{
    switch (command.ToLower())
    {
        case "run":
            DoRun();
            break;
        case "save":
            DoSave();
            break;
        case "quit":
            DoQuit();
            break;
        default:
            InvalidCommand(command);
            break;
    }
}

contoh akhir

Catatan: Seperti operator kesetaraan string (§12.12.8), switch pernyataan peka huruf besar/kecil dan akan menjalankan bagian sakelar tertentu hanya jika string ekspresi pengalihan sama persis dengan case konstanta label. catatan akhir Saat jenis pernyataan yang switch mengatur adalah string atau jenis nilai nullable, nilai null diizinkan sebagai case konstanta label.

statement_listdari switch_block mungkin berisi pernyataan deklarasi (§13,6). Cakupan variabel lokal atau konstanta yang dideklarasikan dalam blok sakelar adalah blok sakelar.

Label sakelar dapat dijangkau jika setidaknya salah satu hal berikut ini benar:

  • Ekspresi pengalihan adalah nilai konstanta dan
    • label adalah case pola yang akan cocok (§11.2.1) nilai tersebut, dan penjaga label tidak ada atau bukan ekspresi konstan dengan nilai false; atau
    • ini adalah default label, dan tidak ada bagian pengalihan yang berisi label kasus yang polanya akan cocok dengan nilai tersebut, dan yang penjaganya tidak ada atau ekspresi konstanta dengan nilai benar.
  • Ekspresi pengalihan bukan nilai konstanta dan baik
    • label adalah case tanpa penjaga atau dengan penjaga yang nilainya bukan false konstanta; atau
    • ini adalah default label dan
      • kumpulan pola yang muncul di antara kasus pernyataan pengalihan yang tidak memiliki penjaga atau memiliki penjaga yang nilainya adalah true konstanta, tidak lengkap (§11,4) untuk jenis tata kelola switch; atau
      • jenis tata kelola sakelar adalah jenis nullable dan kumpulan pola yang muncul di antara kasus pernyataan switch yang tidak memiliki penjaga atau memiliki penjaga yang nilainya adalah true konstanta tidak berisi pola yang akan cocok dengan nilai null.
  • Label sakelar dirujuk oleh pernyataan atau goto case yang dapat dijangkaugoto default.

Daftar pernyataan dari bagian sakelar tertentu dapat dijangkau jika switch pernyataan dapat dijangkau dan bagian sakelar berisi label sakelar yang dapat dijangkau.

Titik switch akhir pernyataan dapat dijangkau jika pernyataan pengalihan dapat dijangkau dan setidaknya salah satu hal berikut ini benar:

  • Pernyataan berisi switch pernyataan yang dapat dijangkau break yang keluar dari switch pernyataan.
  • Tidak ada default label yang ada dan baik
    • Ekspresi pengalihan adalah nilai non-konstanta, dan kumpulan pola yang muncul di antara kasus pernyataan pengalihan yang tidak memiliki penjaga atau memiliki penjaga yang nilainya adalah true konstanta, tidak lengkap (§11,4) untuk jenis tata kelola switch.
    • Ekspresi pengalih adalah nilai non-konstanta dari jenis yang dapat diubah ke null, dan tidak ada pola yang muncul di antara kasus pernyataan pengalihan yang tidak memiliki penjaga atau memiliki penjaga yang nilainya adalah true konstanta akan cocok dengan nilai null.
    • Ekspresi pengalihan adalah nilai konstanta dan tanpa case label tanpa penjaga atau yang penjaganya adalah true konstanta akan cocok dengan nilai tersebut.

Contoh: Kode berikut menunjukkan penggunaan klausa yang when singkat:

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

Kasus var cocok null, string kosong, atau string apa pun yang hanya berisi spasi kosong. contoh akhir

13.9 Pernyataan perulangan

13.9.1 Umum

Pernyataan iterasi berulang kali menjalankan pernyataan yang disematkan.

iteration_statement
    : while_statement
    | do_statement
    | for_statement
    | foreach_statement
    ;

13.9.2 Pernyataan sementara

Pernyataan secara while kondisional menjalankan pernyataan yang disematkan nol atau lebih kali.

while_statement
    : 'while' '(' boolean_expression ')' embedded_statement
    ;

Pernyataan while dijalankan sebagai berikut:

  • boolean_expression (§12.24) dievaluasi.
  • Jika ekspresi Boolean menghasilkan true, kontrol ditransfer ke pernyataan yang disematkan. Kapan dan jika kontrol mencapai titik akhir pernyataan yang disematkan (mungkin dari eksekusi continue pernyataan), kontrol ditransfer ke awal while pernyataan.
  • Jika ekspresi Boolean menghasilkan false, kontrol ditransfer ke titik while akhir pernyataan.

Dalam pernyataan pernyataan yang while disematkan, pernyataan break (§13.10.2) dapat digunakan untuk mentransfer kontrol ke titik while akhir pernyataan (sehingga mengakhiri iterasi pernyataan yang disematkan), dan continue pernyataan (§13.10.3) dapat digunakan untuk mentransfer kontrol ke titik akhir pernyataan yang disematkan (sehingga melakukan iterasi lain dari while pernyataan).

Pernyataan pernyataan yang while disematkan dapat dijangkau jika while pernyataan dapat dijangkau dan ekspresi Boolean tidak memiliki nilai falsekonstanta .

Titik while akhir pernyataan dapat dijangkau jika setidaknya salah satu hal berikut ini benar:

  • Pernyataan berisi while pernyataan yang dapat dijangkau break yang keluar dari while pernyataan.
  • Pernyataan while dapat dijangkau dan ekspresi Boolean tidak memiliki nilai truekonstanta .

13.9.3 Pernyataan do

Pernyataan secara do kondisional menjalankan pernyataan yang disematkan satu atau beberapa kali.

do_statement
    : 'do' embedded_statement 'while' '(' boolean_expression ')' ';'
    ;

Pernyataan do dijalankan sebagai berikut:

  • Kontrol ditransfer ke pernyataan yang disematkan.
  • Kapan dan jika kontrol mencapai titik akhir pernyataan yang disematkan (mungkin dari eksekusi continue pernyataan), boolean_expression (§12,24) dievaluasi. Jika ekspresi Boolean menghasilkan true, kontrol ditransfer ke awal do pernyataan. Jika tidak, kontrol ditransfer ke titik do akhir pernyataan.

Dalam pernyataan pernyataan yang do disematkan, pernyataan break (§13.10.2) dapat digunakan untuk mentransfer kontrol ke titik do akhir pernyataan (sehingga mengakhiri iterasi pernyataan yang disematkan), dan continue pernyataan (§13.10.3) dapat digunakan untuk mentransfer kontrol ke titik akhir pernyataan yang disematkan (sehingga melakukan iterasi lain dari do pernyataan).

Pernyataan pernyataan yang do disematkan dapat dijangkau jika do pernyataan dapat dijangkau.

Titik do akhir pernyataan dapat dijangkau jika setidaknya salah satu hal berikut ini benar:

  • Pernyataan berisi do pernyataan yang dapat dijangkau break yang keluar dari do pernyataan.
  • Titik akhir pernyataan yang disematkan dapat dijangkau dan ekspresi Boolean tidak memiliki nilai truekonstanta .

13.9.4 Untuk pernyataan

Pernyataan mengevaluasi for urutan ekspresi inisialisasi dan kemudian, sementara kondisinya benar, berulang kali menjalankan pernyataan yang disematkan dan mengevaluasi urutan ekspresi perulangan.

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

for_initializer, jika ada, terdiri dari local_variable_declaration (§13.6.2) atau daftar statement_expression(§13,7) yang dipisahkan oleh koma. Cakupan variabel lokal yang dideklarasikan oleh for_initializer adalah for_initializer, for_condition, for_iterator, dan embedded_statement.

For_condition, jika ada, akan menjadi boolean_expression (§12.24).

for_iterator, jika ada, terdiri dari daftar statement_expression(§13,7) yang dipisahkan oleh koma.

Pernyataan for dijalankan sebagai berikut:

  • Jika for_initializer ada, penginisialisasi variabel atau ekspresi pernyataan dijalankan dalam urutan penulisannya. Langkah ini hanya dilakukan sekali.
  • Jika ada for_condition , for_condition dievaluasi.
  • Jika for_condition tidak ada atau jika evaluasi menghasilkan true, kontrol ditransfer ke pernyataan yang disematkan. Kapan dan jika kontrol mencapai titik akhir pernyataan yang disematkan (mungkin dari eksekusi continue pernyataan), ekspresi for_iterator, jika ada, dievaluasi secara berurutan, dan kemudian iterasi lain dilakukan, dimulai dengan evaluasi for_condition pada langkah di atas.
  • Jika for_condition ada dan evaluasi menghasilkan false, kontrol ditransfer ke titik for akhir pernyataan.

Dalam pernyataan pernyataan yang for disematkan, break pernyataan (§13.10.2) dapat digunakan untuk mentransfer kontrol ke titik for akhir pernyataan (dengan demikian mengakhiri iterasi pernyataan yang disematkan), dan pernyataan continue (§13.10.3) dapat digunakan untuk mentransfer kontrol ke titik akhir pernyataan yang disematkan (dengan demikian menjalankan for_iterator dan melakukan iterasi lain dari pernyataan, for dimulai dengan for_condition).

Pernyataan pernyataan yang for disematkan dapat dijangkau jika salah satu hal berikut ini benar:

  • Pernyataan for dapat dijangkau dan tidak ada for_condition yang ada.
  • Pernyataan for dapat dijangkau dan for_condition ada dan tidak memiliki nilai falsekonstanta .

Titik for akhir pernyataan dapat dijangkau jika setidaknya salah satu hal berikut ini benar:

  • Pernyataan berisi for pernyataan yang dapat dijangkau break yang keluar dari for pernyataan.
  • Pernyataan for dapat dijangkau dan for_condition ada dan tidak memiliki nilai truekonstanta .

13.9.5 Pernyataan foreach

13.9.5.1 Umum

Pernyataan ini foreach menghitung elemen koleksi, menjalankan pernyataan yang disematkan untuk setiap elemen koleksi.

foreach_statement
    : 'await'? 'foreach' '(' ref_kind? local_variable_type identifier
      'in' expression ')' embedded_statement
    ;

Local_variable_type dan pengidentifikasi pernyataan foreach menyatakan variabel iterasi pernyataan. var Jika pengidentifikasi diberikan sebagai local_variable_type, dan tidak ada jenis bernama var dalam cakupan, variabel iterasi dikatakan sebagai variabel iterasi yang diketik secara implisit, dan jenisnya diambil untuk menjadi jenis foreach elemen pernyataan, seperti yang ditentukan di bawah ini.

Ini adalah kesalahan waktu kompilasi jika baik await maupun ref_kind ada dalam foreach statement.

Jika foreach_statement berisi atau tidak dan refreadonly, variabel iterasi menunjukkan variabel yang diperlakukan sebagai baca-saja. Jika tidak, jika foreach_statement berisi ref tanpa readonly, variabel iterasi menunjukkan variabel yang akan dapat ditulis.

Variabel iterasi sesuai dengan variabel lokal dengan cakupan yang meluas di atas pernyataan yang disematkan. Selama eksekusi foreach pernyataan, variabel iterasi mewakili elemen koleksi tempat iterasi saat ini sedang dilakukan. Jika variabel iterasi menunjukkan variabel baca-saja, kesalahan waktu kompilasi terjadi jika pernyataan yang disematkan mencoba memodifikasinya (melalui penugasan atau ++ operator dan -- ) atau meneruskannya sebagai parameter referensi atau output.

Pemrosesan waktu foreach kompilasi pernyataan terlebih dahulu menentukan jenis koleksi, jenis enumerator, dan jenis iterasi ekspresi. Pemrosesan untuk foreach pernyataan dirinci dalam §13.9.5.2 dan proses untuk await foreach dirinci dalam §13.9.5.3.

Catatan: Jika ekspresi memiliki nilai null, maka System.NullReferenceException akan terjadi saat run-time. catatan akhir

Implementasi diizinkan untuk menerapkan foreach_statement tertentu secara berbeda; misalnya, karena alasan performa, selama perilaku konsisten dengan ekspansi di atas.

13.9.5.2 Foreach sinkron

Pemrosesan waktu foreach kompilasi pernyataan terlebih dahulu menentukan jenis koleksi, jenis enumerator, dan jenis iterasi ekspresi. Penentuan ini berlanjut sebagai berikut:

  • Jika jenis Xekspresi adalah jenis array, maka ada konversi referensi implisit dari X ke IEnumerable antarmuka (karena System.Array mengimplementasikan antarmuka ini). Jenis koleksi adalah IEnumerable antarmuka, jenis enumerator adalah IEnumerator antarmuka dan jenis iterasi adalah jenis elemen dari jenis Xarray .
  • Jika jenis X ekspresi maka ada konversi implisit dari ekspresi ke dynamic antarmuka (§10.2.10).IEnumerable Jenis koleksi adalah IEnumerable antarmuka dan jenis enumerator adalah IEnumerator antarmuka. var Jika pengidentifikasi diberikan sebagai local_variable_type maka jenis iterasi adalah dynamic, jika tidak, itu adalah object.
  • Jika tidak, tentukan apakah jenis X memiliki metode yang sesuai GetEnumerator :
    • Lakukan pencarian anggota pada jenis X dengan pengidentifikasi GetEnumerator dan tidak ada argumen jenis. Jika pencarian anggota tidak menghasilkan kecocokan, atau menghasilkan ambiguitas, atau menghasilkan kecocokan yang bukan grup metode, periksa antarmuka yang dapat dihitung seperti yang dijelaskan di bawah ini. Disarankan agar peringatan dikeluarkan jika pencarian anggota menghasilkan apa pun kecuali grup metode atau tidak ada kecocokan.
    • Lakukan resolusi kelebihan beban menggunakan grup metode yang dihasilkan dan daftar argumen kosong. Jika resolusi kelebihan beban tidak menghasilkan metode yang berlaku, menghasilkan ambiguitas, atau menghasilkan satu metode terbaik tetapi metode itu statis atau bukan publik, periksa antarmuka yang dapat dihitung seperti yang dijelaskan di bawah ini. Disarankan agar peringatan dikeluarkan jika resolusi kelebihan beban menghasilkan apa pun kecuali metode instans publik yang tidak ambigu atau tidak ada metode yang berlaku.
    • Jika jenis EGetEnumerator pengembalian metode bukan kelas, struct atau jenis antarmuka, kesalahan dihasilkan dan tidak ada langkah lebih lanjut yang diambil.
    • Pencarian anggota dilakukan pada E dengan pengidentifikasi Current dan tidak ada argumen jenis. Jika pencarian anggota tidak menghasilkan kecocokan, hasilnya adalah kesalahan, atau hasilnya adalah apa pun kecuali properti instans publik yang mengizinkan pembacaan, kesalahan dihasilkan dan tidak ada langkah lebih lanjut yang diambil.
    • Pencarian anggota dilakukan pada E dengan pengidentifikasi MoveNext dan tidak ada argumen jenis. Jika pencarian anggota tidak menghasilkan kecocokan, hasilnya adalah kesalahan, atau hasilnya adalah apa pun kecuali grup metode, kesalahan dihasilkan dan tidak ada langkah lebih lanjut yang diambil.
    • Resolusi kelebihan beban dilakukan pada grup metode dengan daftar argumen kosong. Jika resolusi kelebihan beban tidak menghasilkan metode yang berlaku, menghasilkan ambiguitas, atau menghasilkan satu metode terbaik tetapi metode tersebut statis atau tidak publik, atau jenis pengembaliannya bukan bool, kesalahan dihasilkan, dan tidak ada langkah lebih lanjut yang diambil.
    • Jenis koleksi adalah X, jenis enumerator adalah E, dan jenis iterasi adalah jenis Current properti . Properti Current dapat mencakup pengubah ref , dalam hal ini, ekspresi yang dikembalikan adalah variable_reference (§9,5) yang secara opsional baca-saja.
  • Jika tidak, periksa antarmuka yang dapat dijumlahkan:
    • Jika di antara semua jenis Tᵢ yang ada konversi implisit dari X ke IEnumerable<Tᵢ>, ada jenis T unik seperti itu bukan T dan untuk semua yang dynamic lain Tᵢ ada konversi implisit dari IEnumerable<T> ke IEnumerable<Tᵢ>, maka jenis koleksi adalah antarmuka IEnumerable<T>, jenis enumerator adalah antarmuka IEnumerator<T>, dan jenis iterasi adalah T.
    • Jika tidak, jika ada lebih dari satu jenis Ttersebut , maka kesalahan dihasilkan dan tidak ada langkah lebih lanjut yang diambil.
    • Jika tidak, jika ada konversi implisit dari X ke System.Collections.IEnumerable antarmuka, maka jenis koleksi adalah antarmuka ini, jenis enumerator adalah antarmuka System.Collections.IEnumerator, dan jenis iterasi adalah object.
    • Jika tidak, kesalahan dihasilkan, dan tidak ada langkah lebih lanjut yang diambil.

Langkah-langkah di atas, jika berhasil, secara tidak ambigu menghasilkan jenis koleksi, jenis CE enumerator dan jenis Titerasi , , ref Tatau ref readonly T. Pernyataan foreach formulir

foreach (V v in x) «embedded_statement»

kemudian setara dengan:

{
    E e = ((C)(x)).GetEnumerator();
    try
    {
        while (e.MoveNext())
        {
            V v = (V)(T)e.Current;
            «embedded_statement»
        }
    }
    finally
    {
        ... // Dispose e
    }
}

Variabel e tidak terlihat atau dapat diakses oleh ekspresi x atau pernyataan yang disematkan atau kode sumber program lainnya. Variabel v bersifat baca-saja dalam pernyataan yang disematkan. Jika tidak ada konversi eksplisit (§10,3) dari T (jenis iterasi) ke V ( local_variable_type dalam foreach pernyataan), kesalahan dihasilkan dan tidak ada langkah lebih lanjut yang diambil.

Ketika variabel iterasi adalah variabel referensi (§9,7), foreach pernyataan formulir

foreach (ref V v in x) «embedded_statement»

kemudian setara dengan:

{
    E e = ((C)(x)).GetEnumerator();
    try
    {
        while (e.MoveNext())
        {
            ref V v = ref e.Current;
            «embedded_statement»
        }
    }
    finally
    {
        ... // Dispose e
    }
}

Variabel e tidak terlihat atau dapat diakses oleh ekspresi x atau pernyataan yang disematkan atau kode sumber program lainnya. Variabel v referensi adalah baca-tulis dalam pernyataan yang disematkan, tetapi v tidak boleh ditetapkan ulang (§12.21.3). Jika tidak ada konversi identitas (§10.2.2) dari T (jenis iterasi) ke V ( local_variable_type dalam foreach pernyataan), kesalahan dihasilkan dan tidak ada langkah lebih lanjut yang diambil.

Pernyataan foreach formulir foreach (ref readonly V v in x) «embedded_statement» memiliki bentuk yang setara serupa, tetapi variabel v referensi berada ref readonly dalam pernyataan yang disematkan, dan karenanya tidak dapat ditetapkan ulang atau ditetapkan ulang.

Penempatan v di dalam while perulangan penting untuk bagaimana ia ditangkap (§12.19.6.2) oleh fungsi anonim apa pun yang terjadi di embedded_statement.

Contoh:

int[] values = { 7, 9, 13 };
Action f = null;
foreach (var value in values)
{
    if (f == null)
    {
        f = () => Console.WriteLine("First value: " + value);
    }
}
f();

Jika v dalam bentuk yang diperluas dideklarasikan di luar while perulangan, itu akan dibagikan di antara semua iterasi, dan nilainya setelah for perulangan akan menjadi nilai akhir, 13, yang merupakan pemanggilan f akan dicetak. Sebaliknya, karena setiap iterasi memiliki variabelnya vsendiri , yang ditangkap oleh f dalam iterasi pertama akan terus menyimpan nilai 7, yaitu apa yang akan dicetak. (Perhatikan bahwa versi C# sebelumnya yang dinyatakan v di luar perulangan while .)

contoh akhir

finally Isi blok dibangun sesuai dengan langkah-langkah berikut:

  • Jika ada konversi implisit dari E ke System.IDisposable antarmuka, maka

    • Jika E adalah jenis nilai yang tidak dapat diubah ke null, maka finally klausa diperluas ke semantik yang setara dengan:

      finally
      {
          ((System.IDisposable)e).Dispose();
      }
      
    • Jika tidak, finally klausul diperluas ke semantik yang setara dengan:

      finally
      {
          System.IDisposable d = e as System.IDisposable;
          if (d != null)
          {
              d.Dispose();
          }
      }
      

      kecuali bahwa jika E adalah jenis nilai, atau parameter jenis yang dibuat ke jenis nilai, maka konversi ke eSystem.IDisposable tidak akan menyebabkan tinju terjadi.

  • Jika tidak, jika E adalah jenis tertutup, finally klausul diperluas ke blok kosong:

    finally {}
    
  • Jika tidak, finally klausa diperluas ke:

    finally
    {
        System.IDisposable d = e as System.IDisposable;
        if (d != null)
        {
            d.Dispose();
        }
    }
    

Variabel d lokal tidak terlihat atau dapat diakses oleh kode pengguna apa pun. Secara khusus, itu tidak bertentang dengan variabel lain yang cakupannya mencakup finally blok.

Urutan di mana foreach melintasi elemen array, adalah sebagai berikut: Untuk elemen array dimensi tunggal dilalui dalam meningkatkan urutan indeks, dimulai dengan indeks 0 dan berakhir dengan indeks Length – 1. Untuk array multidimensi, elemen dilalui sedih sehingga indeks dimensi paling kanan ditingkatkan terlebih dahulu, lalu dimensi kiri berikutnya, dan seterusnya ke kiri.

Contoh: Contoh berikut mencetak setiap nilai dalam array dua dimensi, dalam urutan elemen:

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

Output yang dihasilkan adalah sebagai berikut:

1.2 2.3 3.4 4.5 5.6 6.7 7.8 8.9

contoh akhir

Contoh: Dalam contoh berikut

int[] numbers = { 1, 3, 5, 7, 9 };
foreach (var n in numbers)
{
    Console.WriteLine(n);
}

jenis n disimpulkan menjadi int, jenis iterasi dari numbers.

contoh akhir

13.9.5.3 menunggu foreach

Pemrosesan waktu foreach kompilasi pernyataan terlebih dahulu menentukan jenis koleksi, jenis enumerator, dan jenis iterasi ekspresi. Pemrosesan untuk foreach pernyataan dirinci dalam §13.9.5.2 dan proses untuk await foreach dirinci dalam §13.9.5.3.

Penentuan ini berlanjut sebagai berikut:

  • Tentukan apakah jenis X memiliki metode yang sesuai GetAsyncEnumerator :
    • Lakukan pencarian anggota pada jenis X dengan pengidentifikasi GetAsyncEnumerator dan tidak ada argumen jenis. Jika pencarian anggota tidak menghasilkan kecocokan, atau menghasilkan ambiguitas, atau menghasilkan kecocokan yang bukan grup metode, periksa antarmuka yang dapat dihitung seperti yang dijelaskan di bawah ini. Disarankan agar peringatan dikeluarkan jika pencarian anggota menghasilkan apa pun kecuali grup metode atau tidak ada kecocokan.
    • Lakukan resolusi kelebihan beban menggunakan grup metode yang dihasilkan dan daftar argumen kosong. Jika resolusi kelebihan beban tidak menghasilkan metode yang berlaku, menghasilkan ambiguitas, atau menghasilkan satu metode terbaik tetapi metode itu statis atau bukan publik, periksa antarmuka yang dapat dihitung seperti yang dijelaskan di bawah ini. Disarankan agar peringatan dikeluarkan jika resolusi kelebihan beban menghasilkan apa pun kecuali metode instans publik yang tidak ambigu atau tidak ada metode yang berlaku.
    • Jika jenis EGetAsyncEnumerator pengembalian metode bukan kelas, struct atau jenis antarmuka, kesalahan dihasilkan dan tidak ada langkah lebih lanjut yang diambil.
    • Pencarian anggota dilakukan pada E dengan pengidentifikasi Current dan tidak ada argumen jenis. Jika pencarian anggota tidak menghasilkan kecocokan, hasilnya adalah kesalahan, atau hasilnya adalah apa pun kecuali properti instans publik yang mengizinkan pembacaan, kesalahan dihasilkan dan tidak ada langkah lebih lanjut yang diambil.
    • Pencarian anggota dilakukan pada E dengan pengidentifikasi MoveNextAsync dan tidak ada argumen jenis. Jika pencarian anggota tidak menghasilkan kecocokan, hasilnya adalah kesalahan, atau hasilnya adalah apa pun kecuali grup metode, kesalahan dihasilkan dan tidak ada langkah lebih lanjut yang diambil.
    • Resolusi kelebihan beban dilakukan pada grup metode dengan daftar argumen kosong. Jika resolusi kelebihan beban tidak menghasilkan metode yang berlaku, menghasilkan ambiguitas, atau menghasilkan satu metode terbaik tetapi metode tersebut statis atau bukan publik, atau jenis pengembaliannya tidak dapat ditunggu (§12.9.8.2) di mana await_expression diklasifikasikan sebagai bool (§12.9.8.3), kesalahan dihasilkan, dan tidak ada langkah lebih lanjut yang diambil.
    • Jenis koleksi adalah X, jenis enumerator adalah E, dan jenis iterasi adalah jenis Current properti .
  • Jika tidak, periksa antarmuka asinkron yang dapat dijumlahkan:
    • Jika di antara semua jenis Tᵢ yang ada konversi implisit dari X ke IAsyncEnumerable<Tᵢ>, ada jenis T unik seperti itu bukan T dan untuk semua yang dynamic lain Tᵢ ada konversi implisit dari IAsyncEnumerable<T> ke IAsyncEnumerable<Tᵢ>, maka jenis koleksi adalah antarmuka IAsyncEnumerable<T>, jenis enumerator adalah antarmuka IAsyncEnumerator<T>, dan jenis iterasi adalah T.
    • Jika tidak, jika ada lebih dari satu jenis Ttersebut , maka kesalahan dihasilkan dan tidak ada langkah lebih lanjut yang diambil.
    • Jika tidak, kesalahan dihasilkan, dan tidak ada langkah lebih lanjut yang diambil.

Langkah-langkah di atas, jika berhasil, secara tidak ambigu menghasilkan jenis koleksi C, jenis enumerator E dan jenis iterasi T. Pernyataan await foreach dalam bentuk

await foreach (V v in x) «embedded_statement»

kemudian setara dengan:

{
    E e = ((C)(x)).GetAsyncEnumerator();
    try
    {
        while (await e.MoveNextAsync())
        {
            V v = (V)(T)e.Current;
            «embedded_statement»
        }
    }
    finally
    {
        ... // Dispose e
    }
}

Variabel e tidak terlihat atau dapat diakses oleh ekspresi x atau pernyataan yang disematkan atau kode sumber program lainnya. Variabel v bersifat baca-saja dalam pernyataan yang disematkan. Jika tidak ada konversi eksplisit (§10,3) dari T (jenis iterasi) ke V ( local_variable_type dalam await foreach pernyataan), kesalahan dihasilkan dan tidak ada langkah lebih lanjut yang diambil.

Enumerator asinkron dapat secara opsional mengekspos sebuah metode DisposeAsync yang dapat dipanggil tanpa argumen dan mengembalikan sesuatu yang dapat diawait dan yang GetResult() mengembalikan void.

Pernyataan foreach formulir

await foreach (T item in enumerable) «embedded_statement»

diperluas ke:

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 Pernyataan lompat

13.10.1 Umum

Pernyataan lompat kontrol transfer tanpa syarat.

jump_statement
    : break_statement
    | continue_statement
    | goto_statement
    | return_statement
    | throw_statement
    ;

Lokasi di mana pernyataan lompat mentransfer kontrol disebut target pernyataan lompat.

Ketika pernyataan lompat terjadi dalam blok, dan target pernyataan lompat itu berada di luar blok itu, pernyataan lompat dikatakan keluar dari blok. Meskipun pernyataan lompat dapat mentransfer kontrol keluar dari blok, pernyataan lompat tidak dapat mentransfer kontrol ke blok.

Eksekusi pernyataan lompat dipersulit dengan adanya pernyataan intervensi try . Dengan tidak adanya try pernyataan seperti itu, pernyataan lompat secara tidak bersyarat mentransfer kontrol dari pernyataan lompat ke targetnya. Di hadapan pernyataan intervensi try seperti itu, eksekusi lebih kompleks. Jika pernyataan lompat keluar dari satu atau beberapa try blok dengan blok terkait finally , kontrol awalnya ditransfer ke finally blok pernyataan terdahulu try . Kapan dan jika kontrol mencapai titik finally akhir blok, kontrol ditransfer ke finally blok pernyataan penutup try berikutnya. Proses ini diulang sampai finally blok semua pernyataan intervensi try telah dijalankan.

Contoh: Dalam kode berikut

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

blok yang finally terkait dengan dua try pernyataan dijalankan sebelum kontrol ditransfer ke target pernyataan lompat. Output yang dihasilkan adalah sebagai berikut:

Before break
Innermost finally block
Outermost finally block
After break

contoh akhir

13.10.2 Pernyataan pembobolan

Pernyataan break keluar dari pernyataan penutup terdekat switch, , while, dofor, atau foreach .

break_statement
    : 'break' ';'
    ;

Target break pernyataan adalah titik akhir dari switchpernyataan penutup terdekat , , while, dofor, atau foreach . break Jika pernyataan tidak diapit oleh switch, , while, do, foratau foreach pernyataan, kesalahan waktu kompilasi terjadi.

Ketika beberapa switchpernyataan , while, do, for, atau foreach disarangkan satu sama lain, break pernyataan hanya berlaku untuk pernyataan terdalu. Untuk mentransfer kontrol di beberapa tingkat bersarang, goto pernyataan (§13.10.4) harus digunakan.

Pernyataan break tidak dapat keluar dari finally blok (§13.11). break Ketika pernyataan terjadi dalam finally blok, target break pernyataan harus berada dalam blok yang samafinally; jika tidak, kesalahan waktu kompilasi terjadi.

Pernyataan break dijalankan sebagai berikut:

  • break Jika pernyataan keluar dari satu atau beberapa try blok dengan blok terkaitfinally, kontrol awalnya ditransfer ke finally blok pernyataan terdahulutry. Kapan dan jika kontrol mencapai titik finally akhir blok, kontrol ditransfer ke finally blok pernyataan penutup try berikutnya. Proses ini diulang sampai finally blok semua pernyataan intervensi try telah dijalankan.
  • Kontrol ditransfer ke target break pernyataan.

break Karena pernyataan secara tanpa syarat mentransfer kontrol di tempat lain, titik break akhir pernyataan tidak pernah dapat dijangkau.

13.10.3 Pernyataan lanjutan

Pernyataan memulai continue iterasi baru dari pernyataan penutup , , while, doatau for terdekatforeach.

continue_statement
    : 'continue' ';'
    ;

Target continue pernyataan adalah titik akhir dari pernyataan yang disematkan dari pernyataan penutup terdekat while, , do, foratau foreach . continue Jika pernyataan tidak diapit oleh whilepernyataan , , do, foratau foreach , terjadi kesalahan waktu kompilasi.

Ketika beberapa whilepernyataan , do, for, atau foreach disarangkan satu sama lain, continue pernyataan hanya berlaku untuk pernyataan terdalu. Untuk mentransfer kontrol di beberapa tingkat bersarang, goto pernyataan (§13.10.4) harus digunakan.

Pernyataan continue tidak dapat keluar dari finally blok (§13.11). continue Ketika pernyataan terjadi dalam finally blok, target continue pernyataan harus berada dalam blok yang samafinally; jika tidak, kesalahan waktu kompilasi terjadi.

Pernyataan continue dijalankan sebagai berikut:

  • continue Jika pernyataan keluar dari satu atau beberapa try blok dengan blok terkaitfinally, kontrol awalnya ditransfer ke finally blok pernyataan terdahulutry. Kapan dan jika kontrol mencapai titik finally akhir blok, kontrol ditransfer ke finally blok pernyataan penutup try berikutnya. Proses ini diulang sampai finally blok semua pernyataan intervensi try telah dijalankan.
  • Kontrol ditransfer ke target continue pernyataan.

continue Karena pernyataan secara tanpa syarat mentransfer kontrol di tempat lain, titik continue akhir pernyataan tidak pernah dapat dijangkau.

13.10.4 Pernyataan goto

Pernyataan mentransfer goto kontrol ke pernyataan yang ditandai oleh label.

goto_statement
    : 'goto' identifier ';'
    | 'goto' 'case' constant_expression ';'
    | 'goto' 'default' ';'
    ;

Target gotopernyataan pengidentifikasi adalah pernyataan berlabel dengan label yang diberikan. Jika label dengan nama yang diberikan tidak ada di anggota fungsi saat ini, atau jika goto pernyataan tidak berada dalam cakupan label, kesalahan waktu kompilasi terjadi.

Catatan: Aturan ini mengizinkan penggunaan goto pernyataan untuk mentransfer kontrol keluar dari cakupan berlapis, tetapi tidak ke dalam cakupan berlapis. Dalam contoh

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

goto pernyataan digunakan untuk mentransfer kontrol keluar dari cakupan berlapis.

catatan akhir

Target pernyataan goto case adalah daftar pernyataan dalam pernyataan penutup switch segera (§13.8.3) yang berisi case label dengan pola konstanta dari nilai konstanta yang diberikan dan tanpa penjaga. goto case Jika pernyataan tidak diapit oleh switch pernyataan, jika pernyataan penutup switch terdekat tidak berisi case, atau jika constant_expression tidak secara implisit dapat dikonversi (§10,2) ke jenis yang mengatur dari pernyataan penutup switch terdekat, kesalahan waktu kompilasi terjadi.

Target pernyataan goto default adalah daftar pernyataan dalam pernyataan yang switch segera diapit (§13.8.3), yang berisi default label. goto default Jika pernyataan tidak diapit oleh switch pernyataan, atau jika pernyataan penutup switch terdekat tidak berisi default label, kesalahan waktu kompilasi terjadi.

Pernyataan goto tidak dapat keluar dari finally blok (§13.11). goto Ketika pernyataan terjadi dalam finally blok, target goto pernyataan harus berada dalam blok yang samafinally, atau jika tidak, terjadi kesalahan waktu kompilasi.

Pernyataan goto dijalankan sebagai berikut:

  • goto Jika pernyataan keluar dari satu atau beberapa try blok dengan blok terkaitfinally, kontrol awalnya ditransfer ke finally blok pernyataan terdahulutry. Kapan dan jika kontrol mencapai titik finally akhir blok, kontrol ditransfer ke finally blok pernyataan penutup try berikutnya. Proses ini diulang sampai finally blok semua pernyataan intervensi try telah dijalankan.
  • Kontrol ditransfer ke target goto pernyataan.

goto Karena pernyataan secara tanpa syarat mentransfer kontrol di tempat lain, titik goto akhir pernyataan tidak pernah dapat dijangkau.

13.10.5 Pernyataan pengembalian

Pernyataan return mengembalikan kontrol ke pemanggil anggota fungsi saat ini di mana pernyataan pengembalian muncul, secara opsional mengembalikan nilai atau variable_reference (§9,5).

return_statement
    : 'return' ';'
    | 'return' expression ';'
    | 'return' 'ref' variable_reference ';'
    ;

return_statement tanpa ekspresi disebut return-no-value; satu yang berisi refekspresi disebut return-by-ref; dan satu yang hanya berisi ekspresi disebut return-by-value.

Ini adalah kesalahan waktu kompilasi untuk menggunakan return-no-value dari metode yang dinyatakan sebagai returns-by-value atau returns-by-ref (§15.6.1).

Ini adalah kesalahan waktu kompilasi untuk menggunakan return-by-ref dari metode yang dinyatakan sebagai returns-no-value atau returns-by-value.

Ini adalah kesalahan kompilasi-waktu untuk menggunakan return-by-value dari metode yang dinyatakan sebagai returns-no-value atau returns-by-ref.

Ini adalah kesalahan waktu kompilasi untuk menggunakan return-by-ref jika ekspresi bukan variable_reference atau merupakan referensi ke variabel yang konteks ref-safe-nya bukan caller-context (§9.7.2).

Ini adalah kesalahan waktu kompilasi untuk menggunakan return-by-ref dari metode yang dideklarasikan dengan method_modifierasync.

Anggota fungsi dikatakan menghitung nilai jika merupakan metode dengan metode returns-by-value (§15.6.11), returns-by-value get accessor dari properti atau pengindeks, atau operator yang ditentukan pengguna. Anggota fungsi yang merupakan returns-no-value tidak menghitung nilai dan merupakan metode dengan jenis voidpengembalian yang efektif, mengatur pengakses properti dan pengindeks, menambahkan dan menghapus pengakses peristiwa, konstruktor instans, konstruktor statis, dan finalizer. Anggota fungsi yang dikembalikan-demi-ref tidak menghitung nilai.

Untuk return-by-value, konversi implisit (§10,2) harus ada dari jenis ekspresi ke jenis pengembalian efektif (§15.6.11) dari anggota fungsi yang berisi. Untuk return-by-ref, konversi identitas (§10.2.2) harus ada antara jenis ekspresi dan jenis pengembalian efektif dari anggota fungsi yang berisi.

return pernyataan juga dapat digunakan dalam isi ekspresi fungsi anonim (§12,19), dan berpartisipasi dalam menentukan konversi mana yang ada untuk fungsi tersebut (§10.7.1).

Ini adalah kesalahan waktu kompilasi agar return pernyataan muncul di finally blok (§13.11).

Pernyataan return dijalankan sebagai berikut:

  • Untuk return-by-value, ekspresi dievaluasi dan nilainya dikonversi ke jenis pengembalian efektif dari fungsi yang berisi oleh konversi implisit. Hasil konversi menjadi nilai hasil yang dihasilkan oleh fungsi . Untuk return-by-ref, ekspresi dievaluasi, dan hasilnya akan diklasifikasikan sebagai variabel. Jika return-by-ref metode penutup menyertakan readonly, variabel yang dihasilkan bersifat baca-saja.
  • return Jika pernyataan diapit oleh satu atau beberapa try blok catch dengan blok terkaitfinally, kontrol awalnya ditransfer ke finally blok pernyataan terdahulutry. Kapan dan jika kontrol mencapai titik finally akhir blok, kontrol ditransfer ke finally blok pernyataan penutup try berikutnya. Proses ini diulang hingga finally blok semua pernyataan penutup try telah dijalankan.
  • Jika fungsi yang berisi bukan fungsi asinkron, kontrol dikembalikan ke pemanggil fungsi yang berisi bersama dengan nilai hasil, jika ada.
  • Jika fungsi yang berisi adalah fungsi asinkron, kontrol dikembalikan ke pemanggil saat ini, dan nilai hasil, jika ada, dicatat dalam tugas pengembalian seperti yang dijelaskan dalam (§15.14.3).

return Karena pernyataan secara tanpa syarat mentransfer kontrol di tempat lain, titik return akhir pernyataan tidak pernah dapat dijangkau.

13.10.6 Pernyataan pelemparan

Pernyataan tersebut throw melemparkan pengecualian.

throw_statement
    : 'throw' expression? ';'
    ;

throw Pernyataan dengan ekspresi melemparkan pengecualian yang dihasilkan dengan mengevaluasi ekspresi. Ekspresi harus secara implisit dapat dikonversi ke System.Exception, dan hasil mengevaluasi ekspresi dikonversi ke System.Exception sebelum dilemparkan. Jika hasil konversi adalah null, akan System.NullReferenceException dilemparkan sebagai gantinya.

Pernyataan throw tanpa ekspresi hanya dapat digunakan dalam catch blok, dalam hal ini, pernyataan tersebut melemparkan kembali pengecualian yang saat ini sedang ditangani oleh blok tersebut catch .

throw Karena pernyataan secara tanpa syarat mentransfer kontrol di tempat lain, titik throw akhir pernyataan tidak pernah dapat dijangkau.

Ketika pengecualian dilemparkan, kontrol ditransfer ke klausul pertama catch dalam pernyataan penutup try yang dapat menangani pengecualian. Proses yang berlangsung dari titik pengecualian dilemparkan ke titik transfer kontrol ke handler pengecualian yang sesuai dikenal sebagai penyebaran pengecualian. Penyebaran pengecualian terdiri dari berulang kali mengevaluasi langkah-langkah berikut sampai catch klausul yang cocok dengan pengecualian ditemukan. Dalam deskripsi ini, titik pelemparan awalnya adalah lokasi di mana pengecualian dilemparkan. Perilaku ini ditentukan dalam (§21.4).

  • Dalam anggota fungsi saat ini, setiap try pernyataan yang mencakup titik lemparan diperiksa. Untuk setiap pernyataan S, dimulai dengan pernyataan terdahulu try dan diakhiri dengan pernyataan terluar try , langkah-langkah berikut dievaluasi:

    • try Jika blok S mencakup titik lemparan dan jika S memiliki satu atau beberapa catch klausul, catch klausa diperiksa dalam urutan penampilan untuk menemukan handler yang cocok untuk pengecualian. Klausa pertama catch yang menentukan jenis T pengecualian (atau parameter jenis yang pada run-time menunjukkan jenis Tpengecualian ) sehingga jenis E run-time yang berasal dianggap T cocok. Jika klausa berisi filter pengecualian, objek pengecualian ditetapkan ke variabel pengecualian, dan filter pengecualian dievaluasi. catch Saat klausa berisi filter pengecualian, catch klausa tersebut dianggap cocok jika filter pengecualian dievaluasi ke true. Klausa umum catch (§13.11) dianggap cocok untuk jenis pengecualian apa pun. Jika klausul yang catch cocok ditemukan, penyebaran pengecualian diselesaikan dengan mentransfer kontrol ke blok catch klausa tersebut.
    • Jika tidak, jika try blok atau catch blok S menutupi titik lemparan dan jika S memiliki finally blok, kontrol ditransfer ke finally blok. finally Jika blok melemparkan pengecualian lain, pemrosesan pengecualian saat ini dihentikan. Jika tidak, ketika kontrol mencapai titik finally akhir blok, pemrosesan pengecualian saat ini dilanjutkan.
  • Jika handler pengecualian tidak terletak di pemanggilan fungsi saat ini, pemanggilan fungsi dihentikan, dan salah satu hal berikut terjadi:

    • Jika fungsi saat ini tidak asinkron, langkah-langkah di atas diulang untuk pemanggil fungsi dengan titik lemparan yang sesuai dengan pernyataan tempat anggota fungsi dipanggil.

    • Jika fungsi saat ini adalah asinkron dan mengembalikan tugas, pengecualian dicatat dalam tugas yang dikembalikan, yang dimasukkan ke dalam status gagal atau dibatalkan seperti yang dijelaskan dalam §15.14.3.

    • Jika fungsi saat ini adalah asinkron dan void-returning, konteks sinkronisasi utas saat ini akan diberi tahu seperti yang dijelaskan dalam §15.14.4.

  • Jika pemrosesan pengecualian mengakhiri semua pemanggilan anggota fungsi di utas saat ini, menunjukkan bahwa utas tidak memiliki handler untuk pengecualian, maka utas itu sendiri dihentikan. Dampak penghentian tersebut ditentukan implementasi.

13.11 Pernyataan coba

Pernyataan ini try menyediakan mekanisme untuk menangkap pengecualian yang terjadi selama eksekusi blok. Selain itu, pernyataan memberikan try kemampuan untuk menentukan blok kode yang selalu dijalankan ketika kontrol meninggalkan try pernyataan.

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
    ;

try_statement terdiri dari kata kunci try diikuti oleh blok, lalu nol atau lebih catch_clauses, lalu finally_clause opsional. Setidaknya akan ada satu catch_clause atau finally_clause.

Dalam exception_specifierjenis, atau kelas dasar yang efektif jika itu adalah type_parameter, adalah System.Exception atau jenis yang berasal darinya.

catch Ketika klausul menentukan class_type dan pengidentifikasi, variabel pengecualian dari nama dan jenis yang diberikan dideklarasikan. Variabel pengecualian dimasukkan ke dalam ruang deklarasi specific_catch_clause (§7,3). Selama eksekusi exception_filter dan catch blok, variabel pengecualian mewakili pengecualian yang saat ini sedang ditangani. Untuk tujuan pemeriksaan penugasan pasti, variabel pengecualian dianggap pasti ditetapkan dalam seluruh cakupannya.

Kecuali klausul catch menyertakan nama variabel pengecualian, tidak mungkin untuk mengakses objek pengecualian di filter dan catch blok.

catch Klausa yang menentukan jenis pengecualian atau nama variabel pengecualian tidak disebut klausul umumcatch. Pernyataan try hanya dapat memiliki satu klausa umum catch , dan, jika ada, itu akan menjadi klausul terakhir catch .

Catatan: Beberapa bahasa pemrograman mungkin mendukung pengecualian yang tidak dapat diwakili sebagai objek yang berasal dari System.Exception, meskipun pengecualian tersebut tidak pernah dapat dihasilkan oleh kode C#. Klausul umum catch mungkin digunakan untuk menangkap pengecualian tersebut. Dengan demikian, klausul umum catch secara semantik berbeda dari yang menentukan jenis System.Exception, di mana yang pertama mungkin juga menangkap pengecualian dari bahasa lain. catatan akhir

Untuk menemukan handler untuk pengecualian, catch klausul diperiksa dalam urutan leksikal. catch Jika klausa menentukan jenis tetapi tidak ada filter pengecualian, itu adalah kesalahan waktu kompilasi untuk klausa yang lebih baru catch dari pernyataan yang sama try untuk menentukan jenis yang sama dengan, atau berasal dari, jenis tersebut.

Catatan: Tanpa pembatasan ini, akan mungkin untuk menulis klausul yang tidak dapat dijangkau catch . catatan akhir

catch Dalam blok, throw pernyataan (§13.10.6) tanpa ekspresi tidak dapat digunakan untuk melemparkan kembali pengecualian yang ditangkap oleh catch blok. Penugasan ke variabel pengecualian tidak mengubah pengecualian yang dilemparkan kembali.

Contoh: Dalam kode berikut

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

metode F ini menangkap pengecualian, menulis beberapa informasi diagnostik ke konsol, mengubah variabel pengecualian, dan melemparkan kembali pengecualian. Pengecualian yang dilemparkan kembali adalah pengecualian asli, sehingga output yang dihasilkan adalah:

Exception in F: G
Exception in Main: G

Jika blok pertama catch telah dilemparkan e alih-alih menumbuhkan kembali pengecualian saat ini, output yang dihasilkan adalah sebagai berikut:

Exception in F: G
Exception in Main: F

contoh akhir

Ini adalah kesalahan waktu kompilasi untuk breakpernyataan , , continueatau goto untuk mentransfer kontrol keluar dari finally blok. breakKetika pernyataan , , continueatau goto terjadi dalam finally blok, target pernyataan harus berada dalam blok yang samafinally, atau jika tidak, kesalahan waktu kompilasi terjadi.

Ini adalah kesalahan waktu kompilasi agar return pernyataan terjadi dalam finally blok.

Ketika eksekusi mencapai try pernyataan, kontrol ditransfer ke try blok. Jika kontrol mencapai titik try akhir blok tanpa pengecualian disebarluaskan, kontrol akan ditransfer ke finally blok jika ada. Jika tidak ada finally blok, kontrol ditransfer ke titik try akhir pernyataan.

Jika pengecualian telah disebarluaskan, catch klausul, jika ada, diperiksa dalam urutan leksikal yang mencari kecocokan pertama untuk pengecualian. Pencarian klausul yang cocok catch berlanjut dengan semua blok penutup seperti yang dijelaskan dalam §13.10.6. catch Klausa cocok jika jenis pengecualian cocok dengan exception_specifier dan exception_filter apa pun benar. catch Klausa tanpa exception_specifier cocok dengan jenis pengecualian apa pun. Jenis pengecualian cocok dengan exception_specifier saat exception_specifier menentukan jenis pengecualian atau jenis dasar dari jenis pengecualian. Jika klausa berisi filter pengecualian, objek pengecualian ditetapkan ke variabel pengecualian, dan filter pengecualian dievaluasi.

Jika pengecualian telah disebarluaskan dan klausul yang cocok catch ditemukan, kontrol ditransfer ke blok pencocokan catch pertama. Jika kontrol mencapai titik catch akhir blok tanpa pengecualian disebarluaskan, kontrol akan ditransfer ke finally blok jika ada. Jika tidak ada finally blok, kontrol ditransfer ke titik try akhir pernyataan. Jika pengecualian telah disebarluaskan dari catch blok, kontrol akan ditransfer ke finally blok jika ada. Pengecualian disebarkan ke pernyataan penutup berikutnya try .

Jika pengecualian telah disebarluaskan, dan tidak ada klausa yang catch cocok yang ditemukan, kontrol akan ditransfer ke finally blok, jika ada. Pengecualian disebarkan ke pernyataan penutup berikutnya try .

Pernyataan finally blok selalu dijalankan ketika kontrol meninggalkan try pernyataan. Ini benar apakah transfer kontrol terjadi sebagai akibat dari eksekusi normal, sebagai akibat dari menjalankan breakpernyataan , , continue, gotoatau return , atau sebagai akibat dari menyebarkan pengecualian dari try pernyataan. Jika kontrol mencapai titik finally akhir blok tanpa pengecualian disebarluaskan, kontrol ditransfer ke titik try akhir pernyataan.

Jika pengecualian dilemparkan selama eksekusi finally blok, dan tidak tertangkap dalam blok yang sama finally , pengecualian disebarkan ke pernyataan penutup try berikutnya. Jika pengecualian lain sedang dalam proses disebarluaskan, pengecualian tersebut akan hilang. Proses penyebaran pengecualian dibahas lebih lanjut dalam deskripsi throw pernyataan (§13.10.6).

Contoh: Dalam kode berikut

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

metode Method melemparkan pengecualian. Tindakan pertama adalah memeriksa klausa penutupcatch, menjalankan filter pengecualian apa pun. Kemudian, finally klausul dalam Method dijalankan sebelum kontrol ditransfer ke klausul pencocokan catch penutup. Output yang dihasilkan adalah:

Filter
Finally
Catch

contoh akhir

try Blok try pernyataan dapat dijangkau jika try pernyataan dapat dijangkau.

catch Blok try pernyataan dapat dijangkau jika try pernyataan dapat dijangkau.

finally Blok try pernyataan dapat dijangkau jika try pernyataan dapat dijangkau.

Titik try akhir pernyataan dapat dijangkau jika kedua hal berikut ini benar:

  • Titik try akhir blok dapat dijangkau atau titik akhir setidaknya satu catch blok dapat dijangkau.
  • Jika ada finally blok, titik finally akhir blok dapat dijangkau.

13.12 Pernyataan yang dicentang dan tidak dicentang

Pernyataan checked dan unchecked digunakan untuk mengontrol konteks pemeriksaan luapan untuk operasi dan konversi aritmatika jenis integral.

checked_statement
    : 'checked' block
    ;

unchecked_statement
    : 'unchecked' block
    ;

Pernyataan menyebabkan checked semua ekspresi dalam blok dievaluasi dalam konteks yang diperiksa, dan unchecked pernyataan menyebabkan semua ekspresi dalam blok dievaluasi dalam konteks yang tidak dicentang.

Pernyataan checked dan unchecked tepatnya setara dengan checked operator dan unchecked (§12.8.20), kecuali bahwa mereka beroperasi pada blok alih-alih ekspresi.

13.13 Pernyataan kunci

Pernyataan ini lock memperoleh kunci pengecualian bersama untuk objek tertentu, menjalankan pernyataan, dan kemudian melepaskan kunci.

lock_statement
    : 'lock' '(' expression ')' embedded_statement
    ;

Ekspresi lock pernyataan harus menunjukkan nilai jenis yang diketahui sebagai referensi. Tidak ada konversi tinju implisit (§10.2.9) yang pernah dilakukan untuk pernyataan, dan dengan demikian itu adalah kesalahan waktu kompilasi bagi ekspresi untuk menunjukkan nilai lock.

Pernyataan lock formulir

lock (x) ...

di mana x adalah ekspresi reference_type, tepatnya setara dengan:

bool __lockWasTaken = false;
try
{
    System.Threading.Monitor.Enter(x, ref __lockWasTaken);
    ...
}
finally
{
    if (__lockWasTaken)
    {
        System.Threading.Monitor.Exit(x);
    }
}

kecuali x hanya dievaluasi sekali.

Saat kunci pengecualian bersama ditahan, kode yang dijalankan dalam utas eksekusi yang sama juga dapat memperoleh dan melepaskan kunci. Namun, kode yang dijalankan di utas lain diblokir agar tidak mendapatkan kunci hingga kunci dilepaskan.

13.14 Pernyataan penggunaan

Pernyataan ini using memperoleh satu atau beberapa sumber daya, menjalankan pernyataan, lalu membuang sumber daya.

using_statement
    : 'using' '(' resource_acquisition ')' embedded_statement
    ;

resource_acquisition
    : local_variable_declaration
    | expression
    ;

Sumber daya adalah kelas atau struct yang mengimplementasikan System.IDisposable antarmuka (IAsyncDisposable untuk aliran asinkron), yang mencakup satu metode tanpa parameter bernama Dispose (DisposeAsync untuk aliran asinkron). Kode yang menggunakan sumber daya dapat memanggil Dispose untuk menunjukkan bahwa sumber daya tidak lagi diperlukan.

Jika bentuk resource_acquisition adalah local_variable_declaration, maka jenis local_variable_declaration haruslah salah satu dari dynamic atau jenis yang dapat dikonversi secara implisit menjadi System.IDisposable (IAsyncDisposable untuk aliran asinkron). Jika bentuk resource_acquisition adalah ekspresi maka ekspresi ini akan secara implisit dapat dikonversi ke System.IDisposable (IAsyncDisposable untuk aliran asinkron).

Variabel lokal yang dinyatakan dalam resource_acquisition bersifat baca-saja, dan harus menyertakan penginisialisasi. Kesalahan waktu kompilasi terjadi jika pernyataan yang disematkan mencoba memodifikasi variabel lokal ini (melalui penugasan atau ++ operator dan -- ), mengambil alamatnya, atau meneruskannya sebagai parameter referensi atau output.

Pernyataan using diterjemahkan ke dalam tiga bagian: akuisisi, penggunaan, dan pembuangan. Penggunaan sumber daya secara implisit diapit dalam try pernyataan yang menyertakan finally klausul. Klausa ini finally membuang sumber daya. null Jika sumber daya diperoleh, maka tidak ada panggilan ke Dispose (DisposeAsync untuk aliran asinkron) yang dilakukan, dan tidak ada pengecualian yang dilemparkan. Jika jenis sumber daya dynamic dikonversi secara dinamis melalui konversi dinamis implisit (§10.2.10) ke IDisposable (IAsyncDisposable untuk aliran asinkron) selama akuisisi untuk memastikan bahwa konversi berhasil sebelum penggunaan dan pembuangan.

Pernyataan using formulir

using (ResourceType resource = «expression» ) «statement»

sesuai dengan salah satu dari tiga kemungkinan ekspansi. Ketika ResourceType adalah jenis nilai yang tidak dapat diubah ke null atau parameter jenis dengan batasan jenis nilai (§15.2.5), ekspansi secara semantik setara dengan

{
    ResourceType resource = «expression»;
    try
    {
        «statement»;
    }
    finally
    {
        ((IDisposable)resource).Dispose();
    }
}

Kecuali bahwa para pemeran resourceSystem.IDisposable untuk tidak menyebabkan tinju terjadi.

Jika tidak, kapan ResourceType adalah dynamic, ekspansinya adalah

{
    ResourceType resource = «expression»;
    IDisposable d = resource;
    try
    {
        «statement»;
    }
    finally
    {
        if (d != null)
        {
            d.Dispose();
        }
    }
}

Jika tidak, ekspansinya adalah

{
    ResourceType resource = «expression»;
    try
    {
        «statement»;
    }
    finally
    {
        IDisposable d = (IDisposable)resource;
        if (d != null)
        {
            d.Dispose();
        }
    }
}

Dalam ekspansi apa pun, resource variabel bersifat baca-saja dalam pernyataan yang disematkan, dan d variabel tidak dapat diakses, dan tidak terlihat, pernyataan yang disematkan.

Implementasi diizinkan untuk menerapkan using_statement tertentu secara berbeda, misalnya, untuk alasan performa, selama perilaku konsisten dengan ekspansi di atas.

Pernyataan using formulir:

using («expression») «statement»

memiliki tiga kemungkinan ekspansi yang sama. Dalam hal ResourceType ini secara implisit adalah jenis ekspresi waktu kompilasi, jika memilikinya. Jika tidak, antarmuka IDisposable (IAsyncDisposable untuk aliran asinkron) itu sendiri digunakan sebagai ResourceType. Variabel resource tidak dapat diakses, dan tidak terlihat, pernyataan yang disematkan.

Ketika resource_acquisition mengambil bentuk local_variable_declaration, dimungkinkan untuk memperoleh beberapa sumber daya dari jenis tertentu. Pernyataan using formulir

using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) «statement»

tepatnya setara dengan urutan pernyataan berlapis using :

using (ResourceType r1 = e1)
using (ResourceType r2 = e2)
...
using (ResourceType rN = eN)
«statement»

Contoh: Contoh di bawah ini membuat file bernama log.txt dan menulis dua baris teks ke file. Contoh kemudian membuka file yang sama untuk membaca dan menyalin baris teks yang terkandung ke konsol.

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

TextWriter Karena kelas dan TextReader mengimplementasikan IDisposable antarmuka, contoh dapat menggunakan using pernyataan untuk memastikan bahwa file yang mendasar ditutup dengan benar setelah operasi tulis atau baca.

contoh akhir

13.15 Pernyataan hasil

Pernyataan yield digunakan dalam blok iterator (§13,3) untuk menghasilkan nilai ke objek enumerator (§15.15.5) atau objek enumerasi (§15.15.6) dari iterator atau untuk menandakan akhir perulangan.

yield_statement
    : 'yield' 'return' expression ';'
    | 'yield' 'break' ';'
    ;

yield adalah kata kunci kontekstual (§6.4.4) dan memiliki arti khusus hanya ketika digunakan segera sebelum kata return kunci atau break .

Ada beberapa batasan di mana pernyataan dapat muncul, seperti yang yield dijelaskan dalam hal berikut.

  • Ini adalah kesalahan waktu kompilasi agar yield pernyataan (dari salah satu formulir) muncul di luar method_body, operator_body, atau accessor_body.
  • Ini adalah kesalahan waktu kompilasi agar yield pernyataan (dari salah satu bentuk) muncul di dalam fungsi anonim.
  • Ini adalah kesalahan waktu kompilasi agar yield pernyataan (dari salah satu formulir) muncul dalam finally klausul try pernyataan.
  • Ini adalah kesalahan waktu kompilasi agar yield return pernyataan muncul di mana saja dalam try pernyataan yang berisi catch_clauses apa pun.

Contoh: Contoh berikut menunjukkan beberapa penggunaan pernyataan yang yield valid dan tidak valid.

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
}

contoh akhir

Konversi implisit (§10,2) harus ada dari jenis ekspresi dalam yield return pernyataan ke jenis hasil (§15.15.4) dari iterator.

Pernyataan yield return dijalankan sebagai berikut:

  • Ekspresi yang diberikan dalam pernyataan dievaluasi, dikonversi secara implisit ke jenis hasil, dan ditetapkan ke Current properti objek enumerator.
  • Eksekusi blok iterator ditangguhkan. yield return Jika pernyataan berada dalam satu atau beberapa try blok, blok terkait finally tidak dijalankan saat ini.
  • Metode MoveNext objek enumerator kembali true ke pemanggilnya, menunjukkan bahwa objek enumerator berhasil dimajukan ke item berikutnya.

Panggilan berikutnya ke metode objek MoveNext enumerator melanjutkan eksekusi blok iterator dari tempat terakhir ditangguhkan.

Pernyataan yield break dijalankan sebagai berikut:

  • yield break Jika pernyataan diapit oleh satu atau beberapa try blok dengan blok terkaitfinally, kontrol awalnya ditransfer ke finally blok pernyataan terdahulutry. Kapan dan jika kontrol mencapai titik finally akhir blok, kontrol ditransfer ke finally blok pernyataan penutup try berikutnya. Proses ini diulang hingga finally blok semua pernyataan penutup try telah dijalankan.
  • Kontrol dikembalikan ke pemanggil blok iterator. Ini adalah MoveNext metode atau Dispose metode objek enumerator.

yield break Karena pernyataan secara tanpa syarat mentransfer kontrol di tempat lain, titik yield break akhir pernyataan tidak pernah dapat dijangkau.