Bagikan melalui


12 Ekspresi

12.1 Umum

Suatu ekspresi adalah urutan operator dan operand. Klausa ini mendefinisikan sintaksis, urutan evaluasi operand dan operator, dan arti ekspresi.

12.2 Klasifikasi ekspresi

12.2.1 Umum

Hasil ekspresi diklasifikasikan sebagai salah satu hal berikut:

  • Suatu nilai. Setiap nilai memiliki jenis terkait.
  • Sebuah variabel. Kecuali ditentukan lain, variabel secara eksplisit diketik dan memiliki jenis terkait, yaitu jenis variabel yang dideklarasikan. Variabel yang di ketik secara implisit tidak memiliki jenis terkait.
  • Literal 'null' Ekspresi dengan klasifikasi ini dapat dikonversi secara implisit ke jenis referensi atau jenis nilai nullable.
  • Fungsi anonim. Ekspresi dengan klasifikasi ini dapat dikonversi secara implisit ke jenis delegasi yang kompatibel atau jenis pohon ekspresi.
  • Sebuah tuple. Setiap tuple terdiri dari jumlah elemen yang tetap, dengan masing-masing elemen memiliki ekspresi dan nama elemen tuple yang opsional.
  • Akses properti. Setiap akses properti memiliki tipe yang terkait, yaitu tipe dari properti tersebut. Selain itu, akses properti mungkin memiliki ekspresi contoh terkait. Ketika aksesor dari akses properti instans dipanggil, hasil dari evaluasi ekspresi instans menjadi instans yang diwakili oleh this (§12.8.14).
  • Akses terhadap pengindeks. Setiap akses pengindeks memiliki jenis terkait, yaitu jenis elemen pengindeks. Selain itu, akses pengindeks memiliki ekspresi instans terkait dan daftar argumen terkait. Ketika pengakses akses pengindeks dipanggil, hasil evaluasi ekspresi instans menjadi instans yang diwakili oleh this (§12.8.14), dan hasil mengevaluasi daftar argumen menjadi daftar parameter pemanggilan.
  • Tidak apa-apa. Ini terjadi ketika ekspresi adalah pemanggilan sebuah metode yang memiliki jenis pengembalian void. Ekspresi yang diklasifikasikan sebagai tidak ada yang hanya valid dalam konteks statement_expression (§13,7) atau sebagai isi lambda_expression (§12,21).

Untuk ekspresi yang terjadi sebagai subekspresi ekspresi yang lebih besar, dengan batasan yang disebutkan, hasilnya juga dapat diklasifikasikan sebagai salah satu hal berikut:

  • Sebuah namespace. Ekspresi dengan klasifikasi ini hanya dapat muncul sebagai sisi kiri member_access (§12,8,7). Dalam konteks lain, ekspresi yang diklasifikasikan sebagai namespace menyebabkan kesalahan waktu kompilasi.
  • Sebuah jenis. Ekspresi dengan klasifikasi ini hanya dapat muncul sebagai sisi kiri member_access (§12,8,7). Dalam konteks lain, ekspresi yang diklasifikasikan sebagai jenis menyebabkan kesalahan waktu kompilasi.
  • Grup metode, yang merupakan sekumpulan metode kelebihan beban yang dihasilkan dari pencarian anggota (§12,5). Grup metode mungkin memiliki ekspresi instans terkait dan daftar argumen jenis terkait. Ketika metode instans dipanggil, hasil evaluasi ekspresi instans menjadi instans yang diwakili oleh this (§12.8.14). Grup metode diizinkan dalam invocation_expression (§12,8,10) atau delegate_creation_expression (§12.8.17.5), dan dapat dikonversi secara implisit ke jenis delegasi yang kompatibel (§10,8). Dalam konteks lain, ekspresi yang diklasifikasikan sebagai grup metode menyebabkan kesalahan waktu kompilasi.
  • Akses acara. Setiap akses peristiwa memiliki jenis terkait, yaitu jenis peristiwa tersebut. Selain itu, akses acara mungkin memiliki ekspresi instance terkait. Akses peristiwa mungkin muncul sebagai operan += kiri operator dan -= (§12.23.5). Dalam konteks lain, ekspresi yang diklasifikasikan sebagai akses ke peristiwa akan menyebabkan kesalahan saat kompilasi. Saat aksesor akses peristiwa instans dipanggil, hasil evaluasi ekspresi instans menjadi instans yang diwakili oleh this (§12.8.14).
  • Ekspresi 'throw', yang dapat digunakan dalam beberapa konteks untuk menghasilkan pengecualian dalam sebuah ekspresi. Ekspresi throw dapat dikonversi dengan konversi implisit ke tipe apa saja.

Akses properti atau akses indeks selalu diklasifikasikan ulang sebagai nilai dengan memanggil aksesor get atau aksesor set. Aksesor tertentu ditentukan oleh konteks akses properti atau pengindeks: Jika akses adalah target penugasan, aksesor yang ditetapkan dipanggil untuk menetapkan nilai baru (§12.23.2). Jika tidak, aksesor get dipanggil untuk mendapatkan nilai saat ini (§12.2.2).

Aksesor instans adalah akses properti pada instans, akses peristiwa pada instans, atau akses pengindeks.

12.2.2 Nilai ekspresi

Sebagian besar konstruksi yang melibatkan ekspresi pada akhirnya mengharuskan ekspresi untuk menunjukkan nilai . Dalam kasus seperti itu, jika ekspresi aktual menunjukkan namespace, jenis, grup metode, atau tidak sama sekali, kesalahan waktu kompilasi terjadi. Namun, jika ekspresi menunjukkan akses properti, akses pengindeks, atau variabel, nilai properti, pengindeks, atau variabel diganti secara implisit:

  • Nilai variabel hanyalah nilai yang saat ini disimpan di lokasi penyimpanan yang diidentifikasi oleh variabel. Variabel harus dianggap pasti ditetapkan (§9,4) sebelum nilainya dapat diperoleh, atau jika tidak, kesalahan waktu kompilasi terjadi.
  • Nilai ekspresi akses properti diperoleh dengan memanggil aksesor get properti. Jika properti tidak memiliki aksesor get, kesalahan waktu kompilasi terjadi. Jika tidak, pemanggilan anggota fungsi (§12,6,6) dilakukan, dan hasil pemanggilan menjadi nilai ekspresi akses properti.
  • Nilai ekspresi akses pengindeks diperoleh dengan memanggil aksesor get pengindeks. Jika pengindeks tidak memiliki aksesor get, kesalahan waktu kompilasi terjadi. Jika tidak, pemanggilan anggota fungsi (§12.6.6) dilakukan dengan daftar argumen yang terkait dengan ekspresi akses pengindeks, dan hasil pemanggilan menjadi nilai ekspresi akses pengindeks.
  • Nilai ekspresi tuple diperoleh melalui penerapan konversi tuple implisit (§10.2.13) pada jenis ekspresi tuple. Kesalahan terjadi ketika mencoba mendapatkan nilai ekspresi tuple yang tidak memiliki jenis.

12.3 Pengikatan Statis dan Dinamis

12.3.1 Umum

pengikatan adalah proses menentukan apa yang dimaksud operasi, berdasarkan jenis atau nilai ekspresi (argumen, operand, penerima). Misalnya, pengikatan panggilan metode ditentukan berdasarkan jenis penerima dan argumen. Pengikatan operator ditentukan berdasarkan jenis operand-nya.

Dalam C# pengikatan operasi biasanya ditentukan pada waktu kompilasi, berdasarkan jenis waktu kompilasi subekspresinya. Demikian juga, jika ekspresi berisi kesalahan, kesalahan terdeteksi dan dilaporkan pada waktu kompilasi. Pendekatan ini dikenal sebagai pengikatan statis .

Namun, jika ekspresi adalah ekspresi dinamis (yaitu, memiliki jenis dynamic) ini menunjukkan bahwa pengikatan apa pun yang diikutinya harus didasarkan pada jenis run-time-nya daripada jenis yang dimilikinya pada waktu kompilasi. Oleh karena itu, pengikatan operasi tersebut ditangguhkan hingga saat operasi tersebut akan dijalankan selama program berjalan. Ini disebut sebagai pengikatan dinamis.

Ketika operasi terikat secara dinamis, sedikit atau tidak ada pemeriksaan yang dilakukan oleh pada waktu kompilasi. Sebaliknya jika pengikatan runtime gagal, kesalahan dilaporkan sebagai pengecualian pada runtime.

Operasi berikut di C# tunduk pada pengikatan:

  • Akses anggota: e.M
  • Pemanggilan metode: e.M(e₁,...,eᵥ)
  • Mendelegasikan pemanggilan: e(e₁,...,eᵥ)
  • Akses elemen: e[e₁,...,eᵥ]
  • Pembuatan objek: baru C(e₁,...,eᵥ)
  • Operator unary yang kelebihan beban: +, -, ! (hanya negasi logis), ~, ++, --, true, false
  • Operator biner yang kelebihan beban: +, -, *, /, %, &, &&, |, ||, ??, ^, <<, >>, ==, !=, >, <, >=, <=
  • Operator penugasan: =, = ref, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, ??=
  • Konversi implisit dan eksplisit

Ketika tidak ada ekspresi dinamis yang terlibat, C# secara default menggunakan pengikatan statis, yang berarti bahwa tipe subekspresi pada waktu kompilasi digunakan dalam proses pemilihan. Namun, ketika salah satu subekspresi dalam operasi yang tercantum di atas adalah ekspresi dinamis, operasi malah terikat secara dinamis.

Ini adalah kesalahan waktu kompilasi jika pemanggilan metode terikat secara dinamis dan salah satu parameter, termasuk penerima, adalah parameter input.

12.3.2 Waktu pengikatan

Pengikatan statis terjadi pada waktu kompilasi, sedangkan pengikatan dinamis terjadi pada run-time. Dalam subklaus berikut, istilah waktu pengikatan mengacu pada waktu kompilasi atau run-time, tergantung pada kapan pengikatan berlangsung.

Contoh: Berikut ini menggambarkan gagasan pengikatan statis dan dinamis dan waktu pengikatan:

object o = 5;
dynamic d = 5;
Console.WriteLine(5); // static binding to Console.WriteLine(int)
Console.WriteLine(o); // static binding to Console.WriteLine(object)
Console.WriteLine(d); // dynamic binding to Console.WriteLine(int)

Dua panggilan pertama terikat secara statis: kelebihan Console.WriteLine dipilih berdasarkan jenis waktu kompilasi argumen mereka. Dengan demikian, waktu pengikatan adalah waktu kompilasi.

Panggilan ketiga terikat secara dinamis: kelebihan Console.WriteLine dipilih berdasarkan jenis run-time argumennya. Ini terjadi karena argumen adalah ekspresi dinamis – jenis waktu kompilasinya dinamis. Dengan demikian, waktu pengikatan untuk panggilan ketiga adalah waktu berjalan.

contoh akhir

12.3.3 Pengikatan dinamis

Subklaus ini bersifat informatif.

Pengikatan dinamis memungkinkan program C# berinteraksi dengan objek dinamis, yaitu, objek yang tidak mengikuti aturan normal sistem jenis C#. Objek dinamis mungkin objek dari bahasa pemrograman lain dengan sistem jenis yang berbeda, atau mungkin merupakan objek yang disiapkan secara terprogram untuk mengimplementasikan semantik pengikatan mereka sendiri untuk operasi yang berbeda.

Mekanisme di mana objek dinamis mengimplementasikan semantiknya sendiri ditentukan implementasi. Antarmuka tertentu – lagi-lagi ditentukan oleh implementasi – diimplementasikan oleh objek dinamis untuk memberi sinyal kepada run-time C# bahwa mereka memiliki makna khusus. Dengan demikian, setiap kali operasi pada objek dinamis terikat secara dinamis, semantik pengikatan mereka sendiri akan menjadi prioritas, bukan semantik C# seperti yang ditentukan dalam spesifikasi ini.

Meskipun tujuan pengikatan dinamis adalah untuk memungkinkan interoperatasi dengan objek dinamis, C# memungkinkan pengikatan dinamis pada semua objek, apakah itu dinamis atau tidak. Ini memungkinkan integrasi objek dinamis yang lebih lancar, karena hasil operasi pada objek tersebut tidak selalu merupakan objek dinamis, tetapi masih merupakan jenis yang tidak diketahui oleh programmer pada waktu kompilasi. Selain itu, pengikatan dinamis dapat membantu menghilangkan kode berbasis refleksi rawan kesalahan bahkan ketika tidak ada objek yang terlibat adalah objek dinamis.

12.3.4 Jenis subekspresi

Ketika operasi terikat secara statis, jenis subekspresi (misalnya, penerima, dan argumen, indeks atau operand) selalu dianggap sebagai jenis waktu kompilasi ekspresi tersebut.

Ketika operasi terikat secara dinamis, jenis subekspresi ditentukan dengan cara yang berbeda tergantung pada jenis waktu kompilasi subekspresi:

  • Subekspresi dengan tipe waktu kompilasi dinamis dianggap memiliki tipe nilai aktual yang dievaluasi oleh ekspresi tersebut pada saat run-time.
  • Subekspresi yang tipe pada saat waktu kompilasinya adalah parameter tipe dianggap memiliki tipe yang diikat dengan parameter tipe selama waktu run-time.
  • Jika tidak, subekspresi dianggap memiliki jenis waktu kompilasinya.

12.4 Operator

12.4.1 Umum

Ungkapan terbentuk dari operand dan operator. Operator dalam suatu ekspresi menunjukkan operasi yang harus diterapkan pada operand.

Contoh: Contoh operator termasuk +, -, *, /, dan new. Contoh operand termasuk literal, bidang, variabel lokal, dan ekspresi. contoh akhir

Ada tiga jenis operator:

  • Operator Unary Operator unary menerima satu operand dan menggunakan notasi prefix (seperti –x) atau notasi postfix (seperti x++).
  • Operator biner. Operator biner mengambil dua operand dan semua menggunakan notasi infiks (seperti x + y).
  • Operator ternary. Hanya satu operator terner, ?:, ada; dibutuhkan tiga operand dan menggunakan notasi infiks (c ? x : y).

Urutan evaluasi operator dalam ekspresi ditentukan oleh presedensi dan asosiativitas operator (§12.4.2).

Operand dalam ekspresi dievaluasi dari kiri ke kanan.

Contoh: Dalam F(i) + G(i++) * H(i), metode F dipanggil menggunakan nilai lama i, maka metode G dipanggil dengan nilai lama i, dan, akhirnya, metode H dipanggil dengan nilai baru i. Ini terlepas dari dan tidak terkait dengan prioritas operator. contoh akhir

Operator tertentu dapat kelebihan beban. Kelebihan beban operator (§12.4.3) memungkinkan implementasi operator yang ditentukan pengguna untuk ditentukan untuk operasi di mana satu atau kedua operand berada dari kelas atau jenis struct yang ditentukan pengguna.

12.4.2 Prioritas operator dan asosiativitas

Ketika pernyataan berisi beberapa operator, prioritas operator mengontrol urutan masing-masing operator dievaluasi.

Catatan: Misalnya, ekspresi x + y * z dievaluasi sebagai x + (y * z) karena operator * memiliki prioritas yang lebih tinggi daripada operator + biner. catatan akhir

Prioritas operator ditentukan oleh definisi produksi tata bahasa yang terkait.

Catatan: Misalnya, additive_expression terdiri dari urutan multiplicative_expressionyang dipisahkan oleh operator + atau -, sehingga memberikan operator + dan - prioritas yang lebih rendah daripada operator *, /, dan %. catatan akhir

Catatan: Tabel berikut ini meringkas semua operator dalam urutan prioritas dari tertinggi hingga terendah:

Subklaus Kategori Operator
§12,8 Utama x.y x?.y f(x) a[x] a?[x] x++ x-- x! new typeof default checked unchecked delegate stackalloc
§12,9 Unari + - !x ~ ^ ++x --x (T)x await x
§12.10 Jangkauan ..
§12.11 Beralih switch { … }
§12.12 bersifat perkalian * / %
§12.12 Aditif + -
§12.13 Pergeseran << >>
§12.14 Pengujian relasi dan tipe < > <= >= is as
§12.14 Kesetaraan == !=
§12.15 DAN Logika &
§12.15 XOR Logika ^
§12.15 ATAU Logika \|
§12.16 Kondisional DAN &&
§12.16 Atau Bersyarat \|\|
§12,17 dan §12,18 Koalesensi null dan ekspresi lemparan ?? throw x
§12.20 Bersyarat ?:
§12,23 dan §12,21 Penugasan dan ekspresi lambda = = ref *= /= %= += -= <<= >>= &= ^= \|= => ??=

catatan akhir

Ketika sebuah operan terjadi di antara dua operator dengan prioritas yang sama, asosiasi operator mengontrol urutan di mana operasi dilakukan:

  • Kecuali untuk operator penugasan, operator rentang, dan operator coalescing null, semua operator biner bersifat asosiatif kiri, yang berarti bahwa operasi dilakukan dari kiri ke kanan.

    Contoh: x + y + z dievaluasi sebagai (x + y) + z. contoh akhir

  • Operator penugasan, operator coalescing null dan operator kondisional (?:) asosiatif kanan, yang berarti bahwa operasi dilakukan dari kanan ke kiri.

    Contoh: x = y = z dievaluasi sebagai x = (y = z). contoh akhir

  • Operator rentang tidak asosiatif, yang berarti bahwa tidak ada operand kiri atau kanan operator rentang yang mungkin range_expression.

    Contoh: Keduanya x..y..z dan x..(y..z) tidak valid seperti .. non-asosiatif. contoh akhir

Prioritas dan asokiativitas dapat dikontrol menggunakan tanda kurung.

Contoh: x + y * z pertama kali mengalikan y dengan z lalu menambahkan hasilnya ke x, tetapi (x + y) * z terlebih dahulu menambahkan x dan y lalu mengalikan hasilnya dengan z. contoh akhir

12.4.3 Kelebihan beban Operator

Semua operator unary dan biner memiliki implementasi yang telah ditentukan sebelumnya. Selain itu, implementasi yang ditentukan pengguna dapat diperkenalkan dengan menyertakan deklarasi operator (§15,10) di kelas dan struktur. Implementasi operator yang ditentukan pengguna selalu diutamakan daripada implementasi operator yang telah ditentukan sebelumnya: Hanya ketika tidak ada implementasi operator yang ditentukan pengguna yang berlaku, implementasi operator yang telah ditentukan sebelumnya akan dipertimbangkan, seperti yang dijelaskan dalam §12,4,4 dan §12,4,5.

Operator unary yang dapat kelebihan bebanadalah:

+ - ! (hanya negasi logis) ~ ++ -- true false

Hanya operator yang tercantum di atas yang dapat kelebihan beban. Secara khusus, tidak mungkin untuk membebani operator pengampunan null (postfix !, §12.8.9) atau operator from-end indeks unary (awalan ^, (§12.9.6)).

Catatan: Meskipun true dan false tidak digunakan secara eksplisit dalam ekspresi (dan oleh karena itu tidak disertakan dalam tabel prioritas dalam §12.4.2), mereka dianggap sebagai operator karena dipanggil dalam beberapa konteks ekspresi: Bo ekspresi (§12,26) dan ekspresi yang melibatkan operator logis kondisional (§12.20) dan kondisional (§12.16). catatan akhir

Operator biner yang dapat kelebihan bebanadalah:

+ - * / % & | ^ << >> == != > < <= >=

Hanya operator yang tercantum di atas yang dapat kelebihan beban. Secara khusus, tidak mungkin untuk membebani akses anggota, pemanggilan metode, atau ..operator , , , =&&||???:=>, checked, unchecked, newtypeofdefault, asdan .is

Ketika operator biner kelebihan beban, operator penetapan majemuk yang sesuai, jika ada, juga secara implisit kelebihan beban.

Contoh: Kelebihan beban operator * juga merupakan kelebihan beban operator *=. Ini dijelaskan lebih lanjut dalam §12.23. contoh akhir

Operator penugasan itu sendiri (=) tidak dapat kelebihan beban. Penugasan selalu melakukan penyimpanan nilai sederhana ke dalam variabel (§12.23.2).

Operasi cast, seperti (T)x, dibebani ulang dengan menyediakan konversi yang ditentukan oleh pengguna (§10.5).

Catatan: Konversi yang ditentukan pengguna tidak memengaruhi perilaku operator is atau as. catatan akhir

Akses elemen, seperti a[x], bukan operator yang dapat di-overload. Sebaliknya, pengindeksan yang ditentukan pengguna didukung melalui pengindeks (§15,9).

Dalam ekspresi, operator direferensikan menggunakan notasi operator, dan dalam deklarasi, operator direferensikan menggunakan notasi fungsi. Tabel berikut menunjukkan hubungan antara operator dan notasi fungsi untuk operator unary dan biner. Dalam entri pertama, «op» menunjukkan operator awalan unary yang dapat kelebihan beban. Pada entri kedua, «op» menunjukkan operator unary postfix ++ dan --. Dalam entri ketiga, «op» menunjukkan operator biner yang dapat kelebihan beban.

Catatan: Untuk contoh kelebihan beban operator ++ dan --, lihat §15.10.2. catatan akhir

Operator Notasi notasi fungsi
«op» x operator «op»(x)
x «op» operator «op»(x)
x «op» y operator «op»(x, y)

Deklarasi operator yang ditentukan pengguna selalu mengharuskan setidaknya salah satu parameter berada di kelas atau jenis struct yang berisi deklarasi operator.

Catatan: Dengan demikian, operator yang ditentukan pengguna tidak dapat memiliki tanda tangan yang sama dengan operator yang telah ditentukan sebelumnya. catatan akhir

Deklarasi operator yang ditentukan pengguna tidak dapat mengubah sintaks, prioritas, atau asokiativitas operator.

Contoh: Operator / selalu merupakan operator biner, selalu memiliki tingkat prioritas yang ditentukan dalam §12.4.2, dan selalu asosiatif kiri. contoh akhir

Catatan: Meskipun operator yang ditentukan pengguna dapat melakukan komputasi apa pun yang diinginkannya, implementasi yang menghasilkan hasil selain yang diharapkan secara intuitif sangat tidak disarankan. Misalnya, implementasi operator == harus membandingkan dua operan untuk kesamaan dan mengembalikan hasil bool yang sesuai. catatan akhir

Deskripsi operator individu dalam §12,9 hingga §12.23 menentukan implementasi operator yang telah ditentukan sebelumnya dan aturan tambahan apa pun yang berlaku untuk setiap operator. Deskripsi menggunakan istilah resolusi kelebihan beban operator unary, resolusi kelebihan beban operator biner, promosi numerik, dan definisi operator lifted yang ditemukan dalam subklausa berikut.

12.4.4 Penyelesaian kelebihan beban operator unary

Operasi formulir «op» x atau x «op», di mana «op» adalah operator unary yang dapat kelebihan beban, dan x adalah ekspresi jenis X, diproses sebagai berikut:

  • Kumpulan operator yang ditentukan pengguna kandidat yang disediakan oleh X untuk operasi operator «op»(x) ditentukan menggunakan aturan §12.4.6.
  • Jika kumpulan operator kandidat yang ditentukan oleh pengguna tidak kosong, maka kumpulan ini menjadi operator kandidat untuk operasi tersebut. Jika tidak, implementasi operator «op» biner yang telah ditentukan sebelumnya, termasuk bentuk terangkat, dijadikan himpunan operator kandidat untuk operasi tersebut. Implementasi operator tertentu yang telah ditentukan sebelumnya ditentukan dalam deskripsi operator. Operator yang telah ditentukan sebelumnya yang disediakan oleh jenis enum atau delegasi hanya disertakan dalam set ini saat jenis waktu pengikatan—atau jenis yang mendasarinya jika merupakan jenis yang dapat diubah ke null—dari salah satu operand adalah jenis enum atau delegasi.
  • Aturan resolusi kelebihan beban §12.6.4 diterapkan ke kumpulan operator kandidat untuk memilih operator terbaik sehubungan dengan daftar argumen (x), dan operator ini menjadi hasil dari proses resolusi kelebihan beban. Jika resolusi kelebihan beban gagal memilih satu operator terbaik, kesalahan waktu pengikatan terjadi.

12.4.5 Resolusi kelebihan beban operator biner

Operasi formulir x «op» y, di mana «op» adalah operator biner yang kelebihan beban, x adalah ekspresi jenis X, dan y adalah ekspresi jenis Y, diproses sebagai berikut:

  • Kumpulan kandidat operator yang didefinisikan oleh pengguna dan disediakan oleh X dan Y untuk operasi operator «op»(x, y) telah ditentukan. Set ini terdiri dari penyatuan operator kandidat yang disediakan oleh X dan operator kandidat yang disediakan oleh Y, masing-masing ditentukan menggunakan aturan §12.4.6. Untuk kumpulan gabungan, kandidat digabungkan sebagai berikut:
    • Jika X dan Y dapat dikonversi identitas, atau jika X dan Y berasal dari jenis dasar umum, operator kandidat bersama hanya terjadi dalam kumpulan gabungan sekali.
    • Jika ada konversi identitas antara X dan Y, operator «op»Y disediakan oleh Y memiliki jenis pengembalian yang sama dengan «op»X yang disediakan oleh X dan jenis operand «op»Y memiliki konversi identitas ke jenis operand «op»X yang sesuai maka hanya «op»X yang terjadi dalam set.
  • Jika kumpulan operator kandidat yang ditentukan oleh pengguna tidak kosong, maka kumpulan ini menjadi operator kandidat untuk operasi tersebut. Jika tidak, implementasi operator «op» biner yang telah ditentukan sebelumnya, termasuk bentuk terangkat, dijadikan himpunan operator kandidat untuk operasi tersebut. Implementasi operator tertentu yang telah ditentukan sebelumnya ditentukan dalam deskripsi operator. Untuk operator enum dan delegasi yang telah ditentukan sebelumnya, satu-satunya operator yang dipertimbangkan adalah operator yang disediakan oleh jenis enum atau delegasi yang merupakan tipe waktu ikat dari salah satu operand.
  • Aturan resolusi kelebihan beban §12.6.4 diterapkan ke kumpulan operator kandidat untuk memilih operator terbaik sehubungan dengan daftar argumen (x, y), dan operator ini menjadi hasil dari proses resolusi kelebihan beban. Jika resolusi kelebihan beban gagal memilih satu operator terbaik, kesalahan waktu pengikatan terjadi.

12.4.6 Calon operator yang ditentukan pengguna

Mengingat jenis T dan operasi operator «op»(A), di mana «op» adalah operator yang dapat kelebihan beban dan A adalah daftar argumen, kumpulan operator kandidat yang ditentukan pengguna yang disediakan oleh T untuk operator «op»(A) ditentukan sebagai berikut:

  • Tentukan jenis T₀. Jika T adalah jenis nilai nullable, T₀ adalah jenis dasarnya; jika bukan, T₀ sama dengan T.
  • Untuk semua deklarasi operator «op» dalam T₀ dan semua bentuk operator yang diangkat, jika setidaknya satu operator berlaku (§12.6.4.2) sehubungan dengan daftar argumen A, maka kumpulan operator kandidat terdiri dari semua operator yang berlaku di T₀.
  • Jika tidak, apabila T₀ adalah object, himpunan operator kandidat kosong.
  • Jika tidak, himpunan operator kandidat yang disediakan oleh T₀ adalah himpunan operator kandidat yang disediakan oleh kelas dasar langsung T₀, atau kelas dasar efektif T₀ jika T₀ adalah parameter tipe.

12.4.7 Promosi numerik

12.4.7.1 Umum

Subklaus ini bersifat informatif.

§12.4.7 serta subpasalnya adalah ringkasan efek gabungan dari:

  • aturan untuk konversi numerik implisit (§10.2.3);
  • aturan untuk konversi yang lebih baik (§12.6.4.7); dan
  • aritmatika yang tersedia (§12,12), relasional (§12,14), dan operator logis integral (§12.15.2).

Promosi numerik terdiri dari melakukan konversi implisit tertentu dari operan pada operator numerik unary dan biner yang telah ditentukan sebelumnya secara otomatis. Promosi numerik bukanlah mekanisme yang berbeda, melainkan efek dari penerapan resolusi kelebihan beban pada operator yang telah ditentukan sebelumnya. Promosi numerik secara khusus tidak memengaruhi evaluasi operator yang ditentukan pengguna, meskipun operator yang ditentukan pengguna dapat diimplementasikan untuk menunjukkan efek serupa.

Sebagai contoh promosi numerik, pertimbangkan implementasi operator * biner yang telah ditentukan sebelumnya:

int operator *(int x, int y);
uint operator *(uint x, uint y);
long operator *(long x, long y);
ulong operator *(ulong x, ulong y);
float operator *(float x, float y);
double operator *(double x, double y);
decimal operator *(decimal x, decimal y);

Ketika aturan resolusi kelebihan beban (§12.6.4) diterapkan ke set operator ini, efeknya adalah memilih operator pertama yang konversi implisitnya ada dari jenis operand.

Contoh: Untuk operasi b * s, di mana b adalah byte dan s adalah short, resolusi kelebihan beban memilih operator *(int, int) sebagai operator terbaik. Dengan demikian, efeknya adalah bahwa b dan s dikonversi ke int, dan jenis hasilnya int. Demikian juga, untuk operasi i * d, di mana i adalah int dan d adalah resolusi double, overload memilih operator *(double, double) sebagai operator terbaik. contoh akhir

Akhir teks informatif.

12.4.7.2 Promosi numerik unari

Subklaus ini bersifat informatif.

Promosi numerik unary terjadi untuk operan operator unary +, -, dan ~ yang telah ditentukan sebelumnya. Promosi numerik unary sekadar terdiri dari mengonversi operan jenis sbyte, byte, short, ushort, atau char ke jenis int. Selain itu, untuk operator unari – , promosi numerik unari mengonversi operan dari jenis uint ke jenis long.

Akhir teks informatif.

12.4.7.3 Promosi numerik biner

Subklaus ini bersifat informatif.

Promosi numerik biner terjadi untuk operan dari operator biner yang telah ditentukan sebelumnya +, -, *, /, %, &, |, ^, ==, !=, >, <, >=, dan <=. Promosi numerik biner secara implisit mengonversi kedua operan ke tipe yang sama yang, dalam kasus operator non-relasional, menjadi jenis yang digunakan sebagai hasil operasi. Promosi biner numerik terdiri dari penerapan aturan berikut, dalam urutan yang disajikan di sini:

  • Jika salah satu operand berjenis decimal, operand lain dikonversi ke jenis decimal, atau kesalahan waktu pengikatan terjadi jika operand lain berjenis float atau double.
  • Jika tidak, jika salah satu operan berjenis double, operand lainnya dikonversi ke jenis double.
  • Jika tidak, jika salah satu operan berjenis float, operand lainnya dikonversi ke jenis float.
  • Jika tidak, jika salah satu operan berjenis ulong, operand lain dikonversi ke jenis ulong, atau kesalahan waktu pengikatan terjadi jika operand lain type sbyte, short, int, atau long.
  • Jika tidak, jika salah satu operan berjenis long, operand lainnya dikonversi ke jenis long.
  • Jika tidak, jika salah satu operan berjenis uint dan operand lainnya berjenis sbyte, short, atau int, kedua operan dikonversi ke jenis long.
  • Jika tidak, jika salah satu operan berjenis uint, operand lainnya dikonversi ke jenis uint.
  • Jika tidak, kedua operan dikonversi ke jenis int.

Catatan: Aturan pertama melarang operasi apa pun yang mencampur jenis decimal dengan jenis double dan float. Aturan ini mengikuti dari fakta bahwa tidak ada konversi implisit antara jenis decimal dan jenis double dan float. catatan akhir

Catatan: Perhatikan juga bahwa tidak mungkin bagi operand memiliki tipe ulong ketika operand lainnya memiliki tipe integral bertanda. Alasannya adalah bahwa tidak ada jenis integral yang dapat mewakili rentang lengkap ulong serta jenis integral yang ditandatangani. catatan akhir

Dalam kedua kasus di atas, ekspresi cast dapat digunakan untuk mengonversi satu operand secara eksplisit ke jenis yang kompatibel dengan operand lainnya.

Contoh: Dalam kode berikut

decimal AddPercent(decimal x, double percent) =>
    x * (1.0 + percent / 100.0);

kesalahan waktu pengikatan terjadi karena decimal tidak dapat dikalikan dengan double. Kesalahan diatasi dengan secara eksplisit mengonversi operand kedua ke decimal, sebagai berikut:

decimal AddPercent(decimal x, double percent) =>
    x * (decimal)(1.0 + percent / 100.0);

contoh akhir

Akhir teks informatif.

12.4.8 Operator yang diangkat

Operator yang diangkat mengizinkan operator yang telah ditentukan dan ditentukan pengguna yang beroperasi pada jenis nilai yang tidak dapat diubah ke null untuk juga digunakan dengan bentuk nullable dari jenis tersebut. Operator yang terangkat dibangun dari operator yang telah ditentukan sebelumnya dan ditentukan oleh pengguna yang memenuhi persyaratan tertentu, seperti yang dijelaskan sebagai berikut:

  • Untuk operator +unary , , ++, -, --!(negasi logis), ^, dan ~, bentuk operator yang diangkat ada jika jenis operand dan hasil keduanya adalah jenis nilai yang tidak dapat diubah ke null. Bentuk yang diangkat dibangun dengan menambahkan pengubah ? tunggal ke tipe operand dan tipe hasil. Operator yang diangkatkan menghasilkan nilai null jika operand adalah null. Jika tidak, operator yang diangkat membuka operand, menerapkan operator dasar, dan membungkus hasilnya.
  • Untuk operator +biner , , -, *, /%, &, |, ^, .., <<dan >>, bentuk operator yang diangkat ada jika jenis operand dan hasil semuanya adalah jenis nilai yang tidak dapat diubah ke null. Bentuk yang diangkat dibangun dengan menambahkan pengubah ? tunggal ke setiap operand dan tipe hasil. Operator yang diangkat menghasilkan null nilai jika satu atau kedua operan (pengecualian adalah null& operator dan | jenisnya bool? , seperti yang dijelaskan dalam §12.15.5). Jika tidak, operator yang dinaikkan membongkar operand, menerapkan operator dasar, dan mengemas hasilnya.
  • Untuk operator kesetaraan == dan !=, bentuk operator yang diangkat ada jika tipe operand merupakan tipe nilai yang tidak dapat bernilai null dan jika jenis hasilnya adalah bool. Formulir yang ditingkatkan dibangun dengan menambahkan pengubah ? tunggal ke setiap jenis operand. Operator yang diangkat menganggap dua nilai null sama, dan nilai null tidak sama dengan nilai non-null apa pun. Jika kedua operan tidaknull, operator yang dinaikkan membuka bungkus operan dan menerapkan operator dasar untuk menghasilkan hasil bool.
  • Untuk operator relasional <, >, <=, dan >=, bentuk operator yang diangkat ada jika jenis operand adalah jenis nilai yang tidak dapat diubah ke null dan jika jenis hasilnya bool. Formulir yang ditingkatkan dibangun dengan menambahkan pengubah ? tunggal ke setiap jenis operand. Operator yang ditingkatkan menghasilkan nilai false jika satu atau kedua operan bernilai null. Jika tidak, operator yang diangkat membuka operand dan menerapkan operator dasar untuk menghasilkan hasil bool.

12.5 Pencarian anggota

12.5.1 Umum

Pencarian anggota adalah proses di mana arti nama dalam konteks suatu tipe ditetapkan. Penelusuran anggota dapat dilakukan sebagai bagian dari evaluasi simple_name (§12.8.4) atau member_access (§12.8.7) dalam ekspresi. Jika simple_name atau member_access terjadi sebagai primary_expression dari invocation_expression (§12.8.10.2), anggota tersebut dikatakan dipanggil.

Jika anggota adalah metode atau peristiwa, atau jika itu adalah konstanta, bidang atau properti dari jenis delegasi (§21) atau jenis dynamic (§8.2.4), maka anggota dikatakan dapat dipanggil.

Pencarian anggota tidak hanya mempertimbangkan nama anggota tetapi juga jumlah parameter jenis yang dimiliki anggota dan apakah anggota dapat diakses. Untuk tujuan pencarian anggota, metode generik dan jenis generik berlapis memiliki jumlah parameter jenis yang ditunjukkan dalam deklarasi masing-masing dan semua anggota lainnya memiliki parameter jenis nol.

Pencarian nama anggota N dengan argumen jenis K dalam jenis T diproses sebagai berikut:

  • Pertama, sekumpulan anggota yang dapat diakses bernama N ditentukan:
    • Jika T adalah parameter jenis, maka set adalah penyatuan kumpulan anggota yang dapat diakses bernama N di setiap jenis yang ditentukan sebagai batasan utama atau batasan sekunder (§15,2,5) untuk T, bersama dengan set anggota yang dapat diakses bernama N di object.
    • Jika tidak, set terdiri dari semua anggota yang dapat diakses (§7,5) yang bernama N di T, termasuk anggota yang diwariskan dan anggota yang dapat diakses bernama N di object. Jika T adalah jenis yang dibangun, kumpulan anggota diperoleh dengan mengganti argumen jenis seperti yang dijelaskan dalam §15.3.3. Anggota yang menyertakan pengubah override dikecualikan dari set.
  • Selanjutnya, jika K nol, semua jenis berlapis yang deklarasinya menyertakan parameter jenis akan dihapus. Jika K bukan nol, semua anggota dengan jumlah parameter jenis yang berbeda akan dihapus. Ketika K nol, metode yang memiliki parameter jenis tidak dihapus, karena proses inferensi jenis (§12.6.3) mungkin dapat menyimpulkan argumen jenis.
  • Selanjutnya, jika anggota dipanggil, semua anggota yang tidak dapat dipanggil akan dihapus dari kumpulan.
  • Selanjutnya, anggota yang disembunyikan oleh anggota lain dihapus dari set. Untuk setiap anggota S.M dalam set, di mana S adalah jenis di mana anggota M dideklarasikan, aturan berikut diterapkan:
    • Jika M adalah anggota konstanta, bidang, properti, peristiwa, atau enumerasi, maka semua anggota yang dideklarasikan dalam jenis dasar S dihapus dari set.
    • Jika M adalah deklarasi jenis, maka semua non-jenis yang dideklarasikan dalam jenis dasar S dihapus dari set, dan semua deklarasi jenis dengan jumlah parameter jenis yang sama seperti M dideklarasikan dalam jenis dasar S dihapus dari set.
    • Jika M adalah metode, maka semua anggota non-metode yang dideklarasikan dalam jenis dasar S dihapus dari set.
  • Selanjutnya, anggota antarmuka yang disembunyikan oleh anggota kelas dihapus dari set. Langkah ini hanya memiliki efek jika T adalah parameter jenis dan T memiliki kelas dasar yang efektif selain object dan set antarmuka efektif yang tidak kosong (§15.2.5). Untuk setiap anggota S.M dalam set, di mana S adalah jenis di mana anggota M dinyatakan, aturan berikut diterapkan jika S adalah deklarasi kelas selain object:
    • Jika M adalah konstanta, bidang, properti, peristiwa, anggota enumerasi, atau deklarasi jenis, maka semua anggota yang dinyatakan dalam deklarasi antarmuka dihapus dari set.
    • Jika M adalah metode, maka semua anggota non-metode yang dinyatakan dalam deklarasi antarmuka dihapus dari set, dan semua metode dengan tanda tangan yang sama seperti M yang dinyatakan dalam deklarasi antarmuka dihapus dari set.
  • Akhirnya, setelah menghapus anggota tersembunyi, hasil pencarian ditentukan:
    • Jika set terdiri dari satu anggota yang bukan metode, maka anggota ini adalah hasil pencarian.
    • Jika tidak, jika set hanya berisi metode, maka grup metode ini adalah hasil pencarian.
    • Jika tidak, pencarian bersifat ambigu, dan terjadi kesalahan waktu pengikatan.

Untuk pencarian anggota dalam jenis selain parameter tipe dan antarmuka, dan pencarian anggota di antarmuka yang murni berpewarisan tunggal (setiap antarmuka dalam rantai pewarisan memiliki tepat nol atau satu antarmuka dasar langsung), efek dari aturan pencarian hanyalah bahwa anggota turunan menyembunyikan anggota dasar dengan nama atau tanda tangan yang sama. Penelusuran pewarisan tunggal seperti itu tidak pernah ambigu. Ambiguitas yang mungkin muncul dari pencarian anggota di antarmuka beberapa warisan dijelaskan dalam §19.4.11.

Catatan: Fase ini hanya mempertimbangkan satu jenis ambiguitas. Jika pencarian anggota menghasilkan grup metode, penggunaan grup metode lebih lanjut mungkin gagal karena ambiguitas, misalnya seperti yang dijelaskan dalam §12.6.4.1 dan §12.6.6.2. catatan akhir

12.5.2 Jenis dasar

Untuk tujuan pencarian anggota, jenis T dianggap memiliki jenis dasar berikut:

  • Jika Tobject atau dynamic, maka T tidak memiliki jenis dasar.
  • Jika T adalah enum_type, jenis dasar T adalah jenis kelas System.Enum, System.ValueType, dan object.
  • Jika T adalah struct_type, jenis dasar T adalah jenis kelas System.ValueType dan object.

    Catatan: nullable_value_type adalah struct_type (§8.3.1). catatan akhir

  • Jika T adalah jenis_kelas, tipe dasar dari T adalah kelas induk dari T, termasuk tipe kelas object.
  • Jika T adalah interface_type, tipe dasar dari T adalah antarmuka dasar dari T dan tipe dari kelas object.
  • Jika T adalah array_type, jenis dasar T adalah jenis kelas System.Array dan object.
  • Jika T adalah delegate_type, jenis dasar T adalah jenis kelas System.Delegate dan object.

12.6 Anggota fungsi

12.6.1 Umum

Anggota fungsi adalah anggota yang berisi pernyataan yang dapat dieksekusi. Anggota fungsi selalu merupakan anggota dari tipe dan tidak dapat menjadi anggota namespace. C# mendefinisikan kategori anggota fungsi berikut:

  • Metode
  • Properti
  • Peristiwa
  • Pengindeks
  • Operator yang ditentukan pengguna
  • Konstruktor instance
  • Konstruktor statis
  • Finalizer

Kecuali untuk finalizer dan konstruktor statis (yang tidak dapat dipanggil secara eksplisit), pernyataan yang terkandung dalam anggota fungsi dijalankan melalui pemanggilan anggota fungsi. Sintaks aktual untuk menulis pemanggilan anggota fungsi tergantung pada kategori anggota fungsi tertentu.

Daftar argumen (§12.6.2) dari pemanggilan anggota fungsi menyediakan nilai aktual atau referensi variabel untuk parameter anggota fungsi.

Pemanggilan metode generik dapat menggunakan inferensi tipe untuk menentukan serangkaian argumen tipe yang akan diteruskan ke metode. Proses ini dijelaskan dalam §12.6.3.

Pemanggilan metode, pengindeks, operator, dan konstruktor instans menggunakan resolusi kelebihan beban untuk menentukan set kandidat anggota fungsi mana yang akan dipanggil. Proses ini dijelaskan dalam §12.6.4.

Setelah anggota fungsi tertentu diidentifikasi pada waktu pengikatan, mungkin melalui resolusi kelebihan beban, proses run-time aktual untuk memanggil anggota fungsi dijelaskan dalam §12.6.6.

Catatan: Tabel berikut ini meringkas pemrosesan yang terjadi dalam konstruksi yang melibatkan enam kategori anggota fungsi yang dapat dipanggil secara eksplisit. Dalam tabel, e, x, y, dan value menunjukkan ekspresi yang diklasifikasikan sebagai variabel atau nilai, T menunjukkan ekspresi yang diklasifikasikan sebagai jenis, F adalah nama sederhana metode, dan P adalah nama sederhana properti.

Membangun Contoh Deskripsi
Pemanggilan metode F(x, y) Resolusi kelebihan beban diterapkan untuk memilih metode terbaik F di kelas atau struktur yang berisi. Metode ini dipanggil dengan daftar argumen (x, y). Jika metodenya bukan static, maka ekspresi instans adalah this.
T.F(x, y) Resolusi kelebihan beban diterapkan untuk memilih metode terbaik F di kelas atau struktur T. Kesalahan waktu pengikatan terjadi jika metode tidak static. Metode ini dipanggil dengan daftar argumen (x, y).
e.F(x, y) Penyelesaian overload diterapkan untuk memilih metode terbaik F dalam kelas, struktur, atau antarmuka yang disebutkan oleh tipe e. Kesalahan waktu pengikatan (binding-time error) terjadi jika metode static. Metode ini dipanggil dengan ekspresi instans e dan daftar argumen (x, y).
Akses properti P Akses pengambil nilai properti P dalam kelas atau struktur tersebut dipanggil. Kesalahan waktu kompilasi terjadi jika P hanya dapat ditulis. Jika P bukan static, ekspresi contoh adalah this.
P = value Aksesor set properti P di kelas yang berisi atau struktur dipanggil dengan daftar argumen (value). Kesalahan waktu kompilasi terjadi jika P bersifat baca-saja. Jika P bukan static, ekspresi contoh adalah this.
T.P Akses get dari properti P di kelas atau struct T dipanggil. Kesalahan waktu kompilasi terjadi jika P bukan static atau jika P hanya dapat ditulis.
T.P = value Aksesor set properti P di kelas atau struktur T dipanggil dengan daftar argumen (value). Kesalahan waktu kompilasi terjadi jika P tidak static atau jika P bersifat baca-saja.
e.P Aksesor get dari properti P dalam kelas, struct, atau antarmuka yang ditentukan oleh tipe E, dipanggil menggunakan ekspresi instance e. Kesalahan waktu pengikatan terjadi jika P adalah static atau jika P hanya dapat ditulis.
e.P = value Aksesor set untuk properti P di dalam kelas, struct, atau antarmuka yang tipe-nya ditentukan oleh E dipanggil dengan ekspresi instans e dan daftar argumen (value). Kesalahan waktu pengikatan terjadi jika Pstatic atau jika P bersifat baca-saja.
Akses acara E += value Akses tambahkan untuk peristiwa E di kelas atau struktur yang berisi dipanggil. Jika E bukan static, ekspresi contoh adalah this.
E -= value Aksesor hapus dari acara E di kelas atau struktur tempat acara itu berada dipanggil. Jika E bukan static, ekspresi contoh adalah this.
T.E += value Akses tambah untuk peristiwa E di kelas atau struktur T dipanggil. Kesalahan waktu pengikatan terjadi jika E tidak static.
T.E -= value Aksesor hapus untuk peristiwa E di dalam kelas atau struktur T telah dipanggil. Kesalahan waktu pengikatan terjadi jika E tidak static.
e.E += value Aksesor penambah dari peristiwa E di dalam kelas, struct, atau antarmuka yang diberikan oleh jenis E dipanggil dengan menggunakan ekspresi instans e. Kesalahan waktu pengikatan terjadi jika E adalah static.
e.E -= value Akses fungsi penghapusan dari event E di dalam kelas, struct, atau antarmuka yang ditentukan oleh tipe E dipanggil menggunakan ekspresi instance e. Kesalahan waktu pengikatan terjadi jika E adalah static.
Akses Pengindeks e[x, y] Resolusi kelebihan beban diterapkan untuk memilih pengindeks terbaik di kelas, struct, atau antarmuka yang diberikan oleh jenis e. Aksesor 'get' dari pengindeks dipanggil dengan ekspresi instance e dan daftar argumen (x, y). Kesalahan waktu pengikatan terjadi jika pengindeks hanya bisa menulis.
e[x, y] = value Resolusi kelebihan beban diterapkan untuk memilih pengindeks terbaik di kelas, struct, atau antarmuka yang diberikan oleh jenis e. Aksesor set pengindeks dipanggil dengan ekspresi instans e dan daftar argumen (x, y, value). Kesalahan waktu pengikatan terjadi jika pengindeks bersifat baca-saja.
Pemanggilan operator -x Resolusi kelebihan beban diterapkan untuk memilih operator unary terbaik di kelas atau struktur yang diberikan oleh jenis x. Operator yang dipilih dipanggil dengan daftar argumen (x).
x + y Resolusi kelebihan beban diterapkan untuk memilih operator biner terbaik di kelas atau struktur yang diberikan oleh jenis x dan y. Operator yang dipilih dipanggil dengan daftar argumen (x, y).
Pemanggilan konstruktor objek new T(x, y) Resolusi kelebihan beban diterapkan untuk memilih konstruktor instans terbaik di kelas atau struct T. Konstruktor instans dipanggil dengan daftar argumen (x, y).

catatan akhir

12.6.2 Daftar argumen

12.6.2.1 Umum

Setiap pemanggilan anggota fungsi dan delegasi menyertakan daftar argumen yang menyediakan nilai aktual atau referensi variabel untuk parameter anggota tersebut. Sintaks untuk menentukan daftar argumen pemanggilan anggota fungsi tergantung pada kategori anggota fungsi:

  • Misalnya konstruktor, metode, pengindeks, dan delegasi, argumen ditentukan sebagai argument_list, seperti yang dijelaskan di bawah ini. Untuk indexer, saat memanggil pengakses set, daftar argumen juga menyertakan ekspresi yang ditentukan sebagai operand kanan dari operator penugasan (assignment operator).

    Catatan: Argumen tambahan ini tidak digunakan untuk resolusi kelebihan beban, hanya selama pemanggilan aksesor yang ditetapkan. catatan akhir

  • Untuk properti, daftar argumen kosong saat memanggil pengakses get, dan terdiri dari ekspresi yang ditentukan sebagai operan kanan operator penetapan saat memanggil pengakses set.
  • Untuk peristiwa, daftar argumen terdiri dari ekspresi yang ditentukan sebagai operand kanan operator += atau -=.
  • Untuk operator yang ditentukan pengguna, daftar argumen terdiri dari operand tunggal operator unary atau dua operan operator biner.

Argumen properti (§15,7) dan peristiwa (§15,8) selalu diteruskan sebagai parameter nilai (§15.6.2.2). Argumen operator yang ditentukan pengguna (§15,10) selalu diteruskan sebagai parameter nilai (§15.6.2.2) atau parameter input (§9.2.8). Argumen pengindeks (§15.9) selalu diteruskan sebagai parameter nilai (§15.6.2.2), parameter input (§9.2.8), atau array parameter (§15.6.2.4). Parameter output dan referensi tidak didukung untuk kategori anggota fungsi ini.

Argumen konstruktor instans, metode, pengindeks, atau pemanggilan delegasi ditentukan sebagai argument_list:

argument_list
    : argument (',' argument)*
    ;

argument
    : argument_name? argument_value
    ;

argument_name
    : identifier ':'
    ;

argument_value
    : expression
    | 'in' variable_reference
    | 'ref' variable_reference
    | 'out' variable_reference
    ;

argument_list terdiri dari satu atau beberapa argumen , dipisahkan oleh koma. Setiap argumen terdiri dari argument_name opsional diikuti oleh argument_value. Argumen dengan argument_name disebut sebagai argumen bernama, sedangkan argumen tanpa argument_name adalah argumen posisi .

argument_value dapat berbentuk salah satu dari berikut ini:

  • Ekspresi , menunjukkan bahwa argumen diteruskan sebagai parameter nilai atau diubah menjadi parameter input lalu diteruskan sebagaimana ditentukan oleh (§12.6.4.2 dan dijelaskan dalam §12.6.2.3.
  • Kata kunci in diikuti oleh variable_reference (§9,5), menunjukkan bahwa argumen diteruskan sebagai parameter input (§15.6.2.3.2). Variabel pasti akan ditetapkan (§9,4) sebelum dapat diteruskan sebagai parameter input.
  • Kata kunci ref diikuti oleh variable_reference (§9,5), menunjukkan bahwa argumen diteruskan sebagai parameter referensi (§15.6.2.3.3). Variabel pasti akan ditetapkan (§9,4) sebelum dapat diteruskan sebagai parameter referensi.
  • Kata kunci out diikuti oleh variable_reference (§9,5), menunjukkan bahwa argumen diteruskan sebagai parameter output (§15.6.2.3.4). Variabel dianggap pasti ditetapkan (§9,4) setelah pemanggilan anggota fungsi di mana variabel diteruskan sebagai parameter output.

Formulir menentukan mode passing-parameter dari argumen: nilai , input , referensi , atau output , masing-masing. Namun, seperti disebutkan di atas, argumen dengan mode pengiriman nilai, mungkin diubah menjadi satu dengan mode pengiriman input.

Melewati bidang volatil (§15.5.4) sebagai parameter input, output, atau referensi menyebabkan peringatan, karena bidang tidak dapat diperlakukan sebagai volatil dengan metode yang dipanggil.

12.6.2.2 Parameter yang sesuai

Untuk setiap argumen dalam daftar argumen harus ada parameter yang sesuai dalam anggota fungsi atau delegasi yang dipanggil.

Daftar parameter yang digunakan dalam hal berikut ditentukan sebagai berikut:

  • Untuk metode virtual dan pengindeks yang ditentukan dalam kelas, daftar parameter dipilih dari deklarasi atau penimpaan pertama fungsi anggota yang ditemukan dengan memulai dari tipe statis penerima, dan mencari melalui kelas dasarnya.
  • Untuk metode parsial, daftar parameter deklarasi metode parsial yang menentukan digunakan.
  • Untuk semua anggota fungsi lainnya dan delegasi hanya ada satu daftar parameter, yang merupakan salah satu yang digunakan.

Posisi argumen atau parameter didefinisikan sebagai jumlah argumen atau parameter yang mendahuluinya dalam daftar argumen atau daftar parameter.

Parameter terkait untuk argumen anggota fungsi ditetapkan sebagai berikut:

  • Argumen dalam argument_list konstruktor instans, metode, pengindeks, dan delegasi:
    • Argumen posisi di mana sebuah parameter berada pada posisi yang sama dalam daftar parameter sesuai dengan parameter tersebut, kecuali jika parameter tersebut adalah larik parameter dan anggota fungsi dipanggil dalam bentuk yang diperluas.
    • Argumen posisi anggota fungsi dengan array parameter yang dipanggil dalam bentuk yang diperluas, yang terjadi pada atau setelah posisi array parameter dalam daftar parameter, sesuai dengan elemen dalam array parameter.
    • Argumen bernama sesuai dengan parameter dengan nama yang sama dalam daftar parameter.
    • Untuk pengindeks, saat memanggil pengakses set, ekspresi yang ditentukan sebagai operand kanan operator penugasan sesuai dengan parameter implisit value dari deklarasi pengakses yang ditetapkan.
  • Untuk properti, saat memanggil aksesor 'get', tidak ada argumen. Saat memanggil pengakses set, ekspresi yang ditentukan sebagai operand kanan operator penugasan sesuai dengan parameter nilai implisit dari deklarasi pengakses set.
  • Untuk operator unary yang didefinisikan oleh pengguna (termasuk konversi), operand tunggal sesuai dengan parameter tunggal deklarasi operator.
  • Untuk operator biner yang ditentukan pengguna, operand kiri sesuai dengan parameter pertama, dan operand kanan sesuai dengan parameter kedua deklarasi operator.
  • Argumen tanpa nama tidak sesuai dengan parameter mana pun setelah argumen bernama yang berada di luar posisi atau argumen bernama yang sesuai dengan array parameter.

    Catatan: Ini mencegah void M(bool a = true, bool b = true, bool c = true); dipanggil oleh M(c: false, valueB);. Argumen pertama digunakan di luar posisi (argumen digunakan dalam posisi pertama, tetapi parameter bernama c berada di posisi ketiga), sehingga argumen berikut harus diberi nama. Dengan kata lain, argumen bernama non-trailing hanya diizinkan ketika nama dan posisi berhasil menemukan parameter yang sama. catatan akhir

12.6.2.3 Evaluasi waktu proses dari daftar argumen

Selama pemrosesan run-time pemanggilan anggota fungsi (§12.6.6), ekspresi atau referensi variabel dari daftar argumen dievaluasi secara berurutan, dari kiri ke kanan, sebagai berikut:

  • Untuk argumen nilai, jika cara pengoperan parameter adalah nilai

    • ekspresi argumen dievaluasi dan konversi implisit (§10,2) ke jenis parameter yang sesuai dilakukan. Nilai yang dihasilkan menjadi nilai awal parameter nilai dalam pemanggilan anggota fungsi.

    • jika tidak, metode pengoperan parameter diterapkan sebagai input. Jika argumen adalah referensi variabel dan ada konversi identitas (§10.2.2) antara jenis argumen dan jenis parameter, lokasi penyimpanan yang dihasilkan menjadi lokasi penyimpanan yang diwakili oleh parameter dalam pemanggilan anggota fungsi. Jika tidak, lokasi penyimpanan dibuat dengan tipe yang sama seperti parameter terkait. Ekspresi argumen dievaluasi dan konversi implisit (§10,2) ke jenis parameter yang sesuai dilakukan. Nilai yang dihasilkan disimpan dalam lokasi penyimpanan tersebut. Lokasi penyimpanan tersebut diwakili oleh parameter input dalam pemanggilan anggota fungsi.

      Contoh: Diberikan deklarasi dan pemanggilan metode berikut:

      static void M1(in int p1) { ... }
      int i = 10;
      M1(i);         // i is passed as an input argument
      M1(i + 5);     // transformed to a temporary input argument
      

      Dalam panggilan metode M1(i), i itu sendiri diteruskan sebagai argumen input, karena diklasifikasikan sebagai variabel dan memiliki jenis int yang sama dengan parameter input. Dalam panggilan metode M1(i + 5), variabel int yang tidak disebutkan namanya dibuat, diinisialisasi dengan nilai argumen, lalu diteruskan sebagai argumen input. Lihat §12.6.4.2 dan §12.6.4.4.

      contoh akhir

  • Untuk argumen input, output, atau referensi, referensi variabel dievaluasi dan lokasi penyimpanan yang dihasilkan menjadi lokasi penyimpanan yang diwakili oleh parameter dalam pemanggilan anggota fungsi. Untuk argumen input atau referensi, variabel harus benar-benar ditetapkan pada titik panggilan metode. Jika referensi variabel diberikan sebagai argumen output, atau merupakan elemen array dari reference_type, pemeriksaan run-time dilakukan untuk memastikan bahwa jenis elemen array identik dengan jenis parameter. Jika pemeriksaan ini gagal, System.ArrayTypeMismatchException akan dilemparkan.

Catatan: pemeriksaan waktu proses ini diperlukan karena kovarians array (Pasal 17.6). catatan akhir

Contoh: Dalam kode berikut

class Test
{
    static void F(ref object x) {...}

    static void Main()
    {
        object[] a = new object[10];
        object[] b = new string[10];
        F(ref a[0]); // Ok
        F(ref b[1]); // ArrayTypeMismatchException
    }
}

pemanggilan kedua F menyebabkan System.ArrayTypeMismatchException dilempar karena jenis elemen aktual b adalah string dan bukan object.

contoh akhir

Metode, pengindeks, dan konstruktor instans dapat menyatakan parameter paling kanan mereka menjadi array parameter (§15.6.2.4). Anggota fungsi tersebut dipanggil baik dalam bentuk normal atau dalam bentuk yang diperluas tergantung pada mana yang berlaku (§12.6.4.2):

  • Ketika anggota fungsi dengan array parameter dipanggil dalam bentuk normalnya, argumen yang diberikan untuk array parameter adalah ekspresi tunggal yang secara implisit dapat dikonversi (§10,2) ke jenis array parameter. Dalam hal ini, array parameter bertindak persis seperti parameter nilai.
  • Ketika anggota fungsi dengan parameter array dipanggil dalam bentuk yang diperluas, pemanggilan harus menentukan satu atau lebih argumen posisional untuk parameter array, di mana setiap argumen adalah ekspresi yang secara implisit dapat dikonversi (§10.2) ke jenis elemen dari parameter array. Dalam hal ini, pemanggilan membuat instans jenis array parameter dengan panjang yang sesuai dengan jumlah argumen, menginisialisasi elemen instans array dengan nilai argumen yang diberikan, dan menggunakan instans array yang baru dibuat sebagai argumen aktual.

Ekspresi daftar argumen selalu dievaluasi dalam urutan tekstual.

Contoh: Dengan demikian, contohnya

class Test
{
    static void F(int x, int y = -1, int z = -2) =>
        Console.WriteLine($"x = {x}, y = {y}, z = {z}");

    static void Main()
    {
        int i = 0;
        F(i++, i++, i++);
        F(z: i++, x: i++);
    }
}

menghasilkan output

x = 0, y = 1, z = 2
x = 4, y = -1, z = 3

contoh akhir

Ketika anggota fungsi dengan array parameter dipanggil dalam bentuk yang diperluas dengan setidaknya satu argumen yang diperluas, pemanggilan diproses seolah-olah ekspresi pembuatan array dengan inisialisasi array (§12.8.17.4) dimasukkan di sekitar argumen yang diperluas. Array kosong diteruskan ketika tidak ada argumen untuk array parameter; tidak ditentukan apakah referensi yang diteruskan adalah ke array kosong yang baru dialokasikan atau sudah ada.

Contoh: Diberikan deklarasi

void F(int x, int y, params object[] args);

pemanggilan metode versi diperluas berikut

F(10, 20, 30, 40);
F(10, 20, 1, "hello", 3.0);

sesuai persis dengan

F(10, 20, new object[] { 30, 40 });
F(10, 20, new object[] { 1, "hello", 3.0 });

contoh akhir

Ketika argumen dihilangkan dari anggota fungsi dengan parameter opsional yang sesuai, argumen default dari deklarasi anggota fungsi diteruskan secara implisit. (Ini dapat melibatkan pembuatan lokasi penyimpanan, seperti yang dijelaskan di atas.)

Catatan: Karena ini selalu konstan, evaluasinya tidak akan berdampak pada evaluasi argumen yang tersisa. catatan akhir

12.6.3 Jenis inferensi

12.6.3.1 Umum

Ketika metode generik dipanggil tanpa menentukan argumen jenis, proses inferensi jenis mencoba menyimpulkan argumen jenis pada pemanggilan. Kehadiran inferensi jenis memungkinkan sintaks yang lebih nyaman untuk digunakan untuk memanggil metode generik, dan memungkinkan programmer untuk menghindari menentukan informasi jenis redundan.

Contoh:

class Chooser
{
    static Random rand = new Random();

    public static T Choose<T>(T first, T second) =>
        rand.Next(2) == 0 ? first : second;
}

class A
{
    static void M()
    {
        int i = Chooser.Choose(5, 213); // Calls Choose<int>
        string s = Chooser.Choose("apple", "banana"); // Calls Choose<string>
    }
}

Dengan inferensi jenis, argumen jenis int dan string ditentukan berdasarkan argumen yang diberikan kepada metode.

contoh akhir

Inferensi tipe terjadi sebagai bagian dari pemrosesan pada saat pengikatan pemanggilan metode (§12.8.10.2) dan terjadi sebelum langkah penyelesaian pemanggilan kelebihan beban. Ketika grup metode tertentu ditentukan dalam pemanggilan metode, dan tidak ada argumen jenis yang ditentukan sebagai bagian dari pemanggilan metode, inferensi jenis diterapkan ke setiap metode generik dalam grup metode. Jika inferensi jenis berhasil, maka argumen jenis yang disimpulkan digunakan untuk menentukan jenis argumen untuk resolusi kelebihan beban berikutnya. Jika resolusi kelebihan beban memilih metode generik sebagai metode yang akan dipanggil, maka argumen jenis yang disimpulkan digunakan sebagai argumen jenis untuk pemanggilan. Jika inferensi jenis untuk metode tertentu gagal, metode tersebut tidak berpartisipasi dalam resolusi kelebihan beban. Kegagalan dalam inferensi tipe, dalam dirinya sendiri, tidak menyebabkan kesalahan waktu penetapan. Namun, sering menyebabkan kesalahan waktu pengikatan ketika resolusi kelebihan beban kemudian gagal menemukan metode yang berlaku.

Jika setiap argumen yang disediakan tidak sesuai dengan tepat satu parameter dalam metode (§12.6.2.2), atau ada parameter non-opsional tanpa argumen yang sesuai, maka inferensi segera gagal. Jika tidak, asumsikan bahwa metode generik memiliki tanda tangan berikut:

Tₑ M<X₁...Xᵥ>(T₁ p₁ ... Tₓ pₓ)

Dengan panggilan metode dalam bentuk M(E₁ ...Eₓ), tugas inferensi tipe adalah menemukan argumen tipe unik S₁...Sᵥ untuk setiap parameter tipe X₁...Xᵥ sehingga panggilan M<S₁...Sᵥ>(E₁...Eₓ) menjadi valid.

Proses inferensi jenis dijelaskan di bawah ini sebagai algoritma. Kompilator yang sesuai dapat diimplementasikan menggunakan pendekatan alternatif, asalkan mencapai hasil yang sama dalam semua kasus.

Selama proses inferensi setiap parameter jenis diteguhkan ke jenis tertentu atau tidak diteguhkan dengan sekumpulan batas yang terkait . Setiap batas adalah jenis tertentu . Awalnya setiap variabel tipe Xᵢ tidak ditetapkan dengan sekumpulan batas kosong.

Inferensi jenis terjadi dalam fase. Setiap fase akan mencoba menyimpulkan argumen jenis untuk variabel jenis lainnya berdasarkan temuan fase sebelumnya. Fase pertama membuat beberapa inferensi awal batas, sedangkan fase kedua memperbaiki variabel jenis ke jenis tertentu dan menyimpulkan batas lebih lanjut. Fase kedua mungkin harus diulang beberapa kali.

Catatan: Inferensi jenis juga digunakan dalam konteks lain termasuk untuk konversi grup metode (§12.6.3.15) dan menemukan jenis ekspresi umum terbaik (§12.6.3.16). catatan akhir

12.6.3.2 Fase pertama

Untuk setiap argumen Eᵢmetode , inferensi jenis input (§12.6.3.7) dibuat dari Eᵢ ke jenis Tⱼparameter yang sesuai .

12.6.3.3 Fase kedua

Fase kedua berlanjut sebagai berikut:

  • Semua variabel jenis Xᵢ yang tidak bergantung pada (§12.6.3.6) apa pun Xₑ diperbaiki (§12.6.3.13).
  • Jika tidak ada variabel jenis seperti itu, semua variabel jenis yang tidak difiks Xᵢ akan difiks dengan ketentuan semua hal berikut terpenuhi:
    • Setidaknya ada satu jenis variabel Xₑ yang bergantung padaXᵢ
    • Xᵢ memiliki sekumpulan batas yang tidak kosong
  • Jika tidak ada variabel jenis tersebut dan masih ada variabel jenis yang belum difiks, inferensi jenis gagal.
  • Jika tidak, jika tidak ada variabel tipe yang belum dipastikan, inferensi tipe berhasil.
  • Jika tidak, untuk semua argumen Eᵢ dengan jenis Tⱼ parameter yang sesuai di mana jenis output (§12.6.3.5) berisi variabel jenis Xₑ tetapi jenis input (§12.6.3.4) tidak, inferensi jenis output (§12.6.3.8) dibuat dariEᵢkeTⱼ. Kemudian fase kedua diulang.

12.6.3.4 Jenis input

Jika E adalah kelompok metode atau fungsi anonim yang diketik secara implisit dan T adalah tipe delegasi atau tipe pohon ekspresi, maka semua tipe parameter dari T adalah tipe input dariEdengan tipeT.

12.6.3.5 Jenis output

Jika adalah grup metode atau fungsi anonim dan adalah jenis delegasi atau jenis pohon ekspresi, maka jenis pengembalian adalah jenis outputdengan jenis.

12.6.3.6 Ketergantungan

Variabel jenis yang tidak difiksXᵢbergantung langsung pada variabel jenis yang tidak difiksXₑ jika untuk beberapa argumen Eᵥ dengan jenis TᵥXₑ terjadi dalam jenis inputEᵥ dengan jenis Tᵥ dan Xᵢ terjadi dalam jenis outputEᵥ dengan jenis Tᵥ.

Xₑ tergantung padaXᵢ jika Xₑtergantung langsung padaXᵢ atau jika Xᵢtergantung langsung padaXᵥ dan Xᵥtergantung padaXₑ. Dengan demikian "tergantung pada" adalah penutupan transitif tetapi tidak refleksif dari "tergantung langsung pada".

12.6.3.7 Inferensi jenis input

Inferensi tipe input dibuat dari sebuah ekspresi Emenjadi sebuah tipe T dengan cara berikut:

  • Jika E adalah ekspresi tuple (§12.8.6) dengan aritas N dan elemen Eᵢ, dan T merupakan jenis tuple dengan aritas N dengan jenis Tₑ elemen yang sesuai atau T merupakan jenis nilai nullable dan T0? merupakan jenis T0 tuple dengan aritas N yang memiliki jenis Tₑelemen yang sesuai , maka untuk setiap Eᵢ, inferensi jenis input dibuat dari Eᵢ ke Tₑ.
  • Jika E adalah fungsi anonim, inferensi jenis parameter eksplisit (§12.6.3.9) dibuat dariEkeT
  • Jika tidak, jika E memiliki jenis U dan parameter yang sesuai adalah parameter nilai (§15.6.2.2) maka inferensi yang terikat lebih rendah (§12.6.3.11) dibuat dariUkeT.
  • Jika tidak, jika E memiliki jenis U dan parameter yang sesuai adalah parameter referensi (§15.6.2.3.3), atau parameter output (§15.6.2.3.4) maka inferensi yang tepat (§12.6.3.10) dibuat dariUkeT.
  • Jika tidak, jika E memiliki jenis U dan parameter yang sesuai adalah parameter input (§15.6.2.3.2) dan E merupakan argumen input, maka inferensi yang tepat (§12.6.3.10) dibuat dariUkeT.
  • Jika tidak, jika E memiliki jenis U dan parameter yang sesuai adalah parameter input (§15.6.2.3.2) maka inferensi terikat yang lebih rendah (§12.6.3.11) dibuat dariUkeT.
  • Jika tidak, tidak ada inferensi yang dibuat untuk argumen ini.

12.6.3.8 Inferensi jenis output

inferensi tipe keluaran dibuat dari sebuah ekspresi ke sebuah tipe dengan cara berikut:

  • Jika E adalah ekspresi tuple dengan aritas N dan elemen Eᵢ, dan T merupakan jenis tuple dengan aritas N dengan jenis Tₑ elemen yang sesuai atau T merupakan jenis nilai nullable dan T0? merupakan jenis T0 tuple dengan aritas N yang memiliki jenis Tₑelemen yang sesuai , maka untuk setiap Eᵢ inferensi jenis output dibuat dari Eᵢ ke Tₑ.
  • Jika E adalah fungsi anonim dengan tipe U pengembalian yang disimpulkan (§12.6.3.14) dan T merupakan tipe delegasi atau jenis pohon ekspresi dengan jenis Tₓpengembalian, maka inferensi yang terikat lebih rendah (§12.6.3.11) dibuat dariUkeTₓ.
  • Jika tidak, jika E adalah kelompok metode dan T adalah jenis delegasi atau jenis pohon ekspresi dengan jenis parameter T₁...Tᵥ dan jenis pengembalian Tₓ, dan resolusi overload dari E dengan jenis T₁...Tᵥ menghasilkan metode tunggal dengan jenis pengembalian U, maka inferensi batas bawah dibuat dariUkeTₓ.
  • Jika tidak, jika E adalah ekspresi dengan jenis U, maka inferensi terikat bawah dibuat dariUkeT.
  • Jika tidak, tidak ada inferensi yang dibuat.

12.6.3.9 Inferensi jenis parameter eksplisit

inferensi jenis parameter eksplisit dibuat dari ekspresi untuk jenis dengan cara berikut:

  • Jika E adalah fungsi anonim yang diketik secara eksplisit dengan jenis U₁...Uᵥ parameter dan T merupakan jenis delegasi atau jenis pohon ekspresi dengan jenis V₁...Vᵥ parameter maka untuk setiap Uᵢinferensi yang tepat (§12.6.3.10) dibuat dariUᵢke yang sesuai Vᵢ.

12.6.3.10 Inferensi yang tepat

Inferensi yang tepat dari tipe ke tipe U dibuat sebagai berikut:

  • Jika V adalah salah satu dari Xᵢ yang belum diperbaiki, maka U ditambahkan ke dalam kumpulan batas yang tepat untuk Xᵢ.
  • Jika tidak, set V₁...Vₑ dan U₁...Uₑ ditentukan dengan memeriksa apakah salah satu kasus berikut berlaku:
    • V adalah jenis array V₁[...] dan U adalah jenis array U₁[...] dengan peringkat yang sama
    • V adalah jenis V₁? dan U adalah jenis U₁
    • V adalah jenis yang dibentuk C<V₁...Vₑ> dan U adalah jenis yang dibentuk C<U₁...Uₑ>
      Jika salah satu kasus ini berlaku maka inferensi persis dibuat dari setiap ke Uᵢ yang sesuai.
  • Jika tidak, tidak ada inferensi yang dibuat.

12.6.3.11 Inferensi terikat bawah

Inferensi batas bawah dari jenis U dibuat sebagai berikut:

  • Jika V adalah salah satu dari Xᵢ yang belum diperbaiki, maka U ditambahkan ke dalam set batas bawah untuk Xᵢ.
  • Jika tidak, jika V adalah jenis V₁? dan U adalah jenis U₁? maka inferensi terikat yang lebih rendah dibuat dari U₁ ke V₁.
  • Jika tidak, set U₁...Uₑ dan V₁...Vₑ ditentukan dengan memeriksa apakah salah satu kasus berikut berlaku:
    • V adalah jenis array V₁[...]dan U adalah jenis array U₁[...]dengan peringkat yang sama
    • V adalah salah satu dari IEnumerable<V₁>, ICollection<V₁>, IReadOnlyList<V₁>>, IReadOnlyCollection<V₁>, atau IList<V₁>, dan U adalah jenis array satu dimensi U₁[]
    • V adalah jenis class, struct, interface, atau delegate dari C<V₁...Vₑ> yang dibangun dan ada jenis unik C<U₁...Uₑ> sedemikian rupa sehingga U (atau, jika U adalah jenis parameter, kelas dasar yang efektif atau anggota set antarmuka efektifnya) identik dengan, inherits dari (secara langsung atau tidak langsung), atau mengimplementasikan (secara langsung atau tidak langsung) C<U₁...Uₑ>.
    • (Pembatasan "keunikan" berarti bahwa dalam kasus antarmuka C<T>{} class U: C<X>, C<Y>{}, maka tidak ada inferensi yang dibuat ketika menyimpulkan dari U ke C<T> karena U₁ bisa X atau Y.)
      Jika salah satu kasus ini berlaku, inferensi dibuat dari setiap Uᵢ ke Vᵢ yang sesuai sebagai berikut:
    • Jika Uᵢ tidak diketahui sebagai jenis referensi, maka dibuat inferensi yang tepat
    • Jika tidak, jika U adalah jenis array, maka penyimpulan tentang batas bawah dilakukan.
    • Jika tidak, jika V adalah C<V₁...Vₑ> maka inferensi tergantung pada parameter tipe i-th dari C:
      • Jika kovarian, maka akan dibuat inferensi batas bawah.
      • Jika kontravarian maka inferensi batas atas dibuat.
      • Jika bersifat invarian maka inferensi yang tepat dibuat.
  • Jika tidak, tidak ada inferensi yang dibuat.

12.6.3.12 Inferensi terikat atas

Inferensi batas atas dari jenis Uke jenis V dibuat sebagai berikut:

  • Jika adalah salah satu yang belum difiks maka ditambahkan ke kumpulan batas atas untuk .
  • Jika tidak, set V₁...Vₑ dan U₁...Uₑ ditentukan dengan memeriksa apakah salah satu kasus berikut berlaku:
    • U adalah jenis array U₁[...]dan V adalah jenis array V₁[...]dengan peringkat yang sama
    • U adalah salah satu dari IEnumerable<Uₑ>, ICollection<Uₑ>, IReadOnlyList<Uₑ>, IReadOnlyCollection<Uₑ>, atau IList<Uₑ>, dan V adalah jenis array satu dimensi Vₑ[]
    • U adalah jenis U1? dan V adalah jenis V1?
    • U adalah jenis kelas, struktur, antarmuka, atau delegasi yang dibangun dan C<U₁...Uₑ> dan V adalah jenis class, struct, interface atau delegate yang identical ke, inherits dari (secara langsung atau tidak langsung), atau mengimplementasikan (secara langsung atau tidak langsung) jenis unik C<V₁...Vₑ>.
    • (Pembatasan "keunikan" berarti bahwa memberikan antarmuka C<T>{} class V<Z>: C<X<Z>>, C<Y<Z>>{}, maka tidak ada inferensi yang dibuat ketika menyimpulkan dari C<U₁> ke V<Q>. Inferensi tidak dibuat dari U₁ ke X<Q> atau Y<Q>.)
      Jika salah satu kasus ini berlaku, inferensi dibuat dari setiap Uᵢ ke Vᵢ yang sesuai sebagai berikut:
    • Jika Uᵢ tidak diketahui sebagai jenis referensi, maka dibuat inferensi yang tepat
    • Jika tidak, apabila V adalah tipe array, maka inferensi terikat atas dilakukan.
    • Jika tidak, jika U adalah C<U₁...Uₑ> maka inferensi tergantung pada parameter tipe i-th dari C:
      • Jika kovarian, maka dibuat inferensi batas atas .
      • Jika bersifat kontravarian, maka dibuat inferensi batas bawah .
      • Jika bersifat invarian maka inferensi yang tepat dibuat.
  • Jika tidak, tidak ada inferensi yang dibuat.

12.6.3.13 Perbaikan

Variabel tipe yang belum ditetapkan dengan serangkaian batas akan ditetapkan sebagai berikut:

  • Kumpulan jenis kandidat Uₑ dimulai sebagai kumpulan semua jenis dalam rentang batas untuk Xᵢ.
  • Setiap batas untuk Xᵢ diperiksa secara bergantian: Untuk setiap batas tepat U dari Xᵢ, maka semua jenis Uₑ yang tidak identik dengan U dikeluarkan dari kumpulan kandidat. Untuk setiap batas bawah U dari Xᵢ semua jenis Uₑ yang tidak memiliki konversi implisit dari dihapus dari set kandidat. Untuk setiap batas atas U dari Xᵢ, semua tipe Uₑ yang tidak memiliki konversi implisit ke dihapus dari kumpulan kandidat.
  • Jika di antara jenis kandidat yang tersisa Uₑ ada jenis unik V di mana ada konversi implisit dari semua jenis kandidat lainnya, maka Xᵢ diatur menjadi V.
  • Jika tidak, penentuan jenis gagal.

12.6.3.14 Jenis pengembalian yang disimpulkan

Jenis pengembalian yang disimpulkan dari fungsi anonim F digunakan selama inferensi jenis dan resolusi kelebihan beban. Jenis pengembalian yang disimpulkan hanya bisa ditentukan untuk fungsi anonim di mana semua jenis parameter sudah diketahui, baik karena telah ditentukan secara eksplisit, diberikan melalui konversi fungsi anonim, atau disimpulkan selama inferensi tipe pada pemanggilan metode generik yang mengelilinginya.

Tipe pengembalian efektif yang disimpulkan ditentukan sebagai berikut:

  • Jika isi F adalah ekspresi yang memiliki jenis, maka jenis pengembalian efektif F yang disimpulkan adalah jenis ekspresi tersebut.
  • Jika isi F adalah blok dan kumpulan ekspresi dalam pernyataan blok return memiliki jenis T umum terbaik (§12.6.3.16), maka jenis pengembalian efektif yang F disimpulkan adalah T.
  • Jika tidak, jenis pengembalian yang efektif tidak dapat disimpulkan untuk F.

Jenis pengembalian yang disimpulkan ditentukan sebagai berikut:

  • Jika F asinkron dan isinya F adalah ekspresi yang diklasifikasikan sebagai tidak ada (§12.2), atau blok di mana tidak ada return pernyataan yang memiliki ekspresi, jenis pengembalian yang disimpulkan adalah «TaskType» (§15.14.1).
  • Jika F asinkron dan memiliki jenis Tpengembalian efektif yang disimpulkan , jenis pengembalian yang disimpulkan adalah «TaskType»<T>»(§15.14.1).
  • Jika F tidak bersifat asinkron dan memiliki jenis pengembalian efektif yang disimpulkan sebagai T, maka jenis pengembalian yang disimpulkan adalah T.
  • Sebaliknya, tipe pengembalian tidak dapat disimpulkan untuk F.

Contoh: Sebagai contoh inferensi jenis yang melibatkan fungsi anonim, pertimbangkan metode ekstensi Select yang dideklarasikan di kelas System.Linq.Enumerable:

namespace System.Linq
{
    public static class Enumerable
    {
        public static IEnumerable<TResult> Select<TSource,TResult>(
            this IEnumerable<TSource> source,
            Func<TSource,TResult> selector)
        {
            foreach (TSource element in source)
            {
                yield return selector(element);
            }
        }
   }
}

Dengan asumsi namespace System.Linq diimpor dengan arahan using namespace, dan sebuah kelas Customer dengan properti berjenis Namestring, metode Select dapat digunakan untuk memilih nama-nama dari daftar pelanggan.

List<Customer> customers = GetCustomerList();
IEnumerable<string> names = customers.Select(c => c.Name);

Pemanggilan metode ekstensi (§12.8.10.3) dari Select diproses dengan menulis ulang pemanggilan tersebut menjadi pemanggilan metode statis:

IEnumerable<string> names = Enumerable.Select(customers, c => c.Name);

Karena argumen jenis tidak ditentukan secara eksplisit, inferensi jenis digunakan untuk menyimpulkan argumen jenis. Pertama, argumen pelanggan terkait dengan parameter sumber, menyimpulkan bahwa TSource adalah Customer. Kemudian, menggunakan proses inferensi jenis fungsi anonim yang dijelaskan di atas, c diberi jenis Customer, dan ekspresi c.Name terkait dengan jenis pengembalian parameter pemilih, menyimpulkan TResult menjadi string. Dengan demikian, pemanggilan setara dengan

Sequence.Select<Customer,string>(customers, (Customer c) => c.Name)

dan hasilnya adalah jenis IEnumerable<string>.

Contoh berikut menunjukkan bagaimana inferensi tipe fungsi anonim memungkinkan informasi tipe untuk "mengalir" antara argumen dalam pemanggilan metode generik. Mengingat metode dan pemanggilan berikut ini:

class A
{
    static Z F<X,Y,Z>(X value, Func<X,Y> f1, Func<Y,Z> f2)
    {
        return f2(f1(value));
    }

    static void M()
    {
        double hours = F("1:15:30", s => TimeSpan.Parse(s), t => t.TotalHours);
    }
}

penyimpulan tipe untuk pemanggilan berlangsung sebagai berikut: Pertama, argumen "1:15:30" terkait dengan parameter nilai, menyimpulkan X menjadi string. Kemudian, parameter fungsi anonim pertama, s, diberi jenis yang disimpulkan string, dan ekspresi TimeSpan.Parse(s) terkait dengan jenis pengembalian f1, menyimpulkan Y akan System.TimeSpan. Akhirnya, parameter fungsi anonim kedua, t, diberi jenis yang disimpulkan System.TimeSpan, dan ekspresi t.TotalHours terkait dengan jenis pengembalian f2, menyimpulkan Z akan double. Dengan demikian, hasil pemanggilan tersebut berjenis double.

contoh akhir

12.6.3.15 Jenis inferensi untuk konversi grup metode

Mirip dengan panggilan metode generik, inferensi jenis juga harus diterapkan ketika grup metode M berisi metode generik dikonversi ke jenis delegasi tertentu D (§10,8). Diberi metode

Tₑ M<X₁...Xᵥ>(T₁ x₁ ... Tₑ xₑ)

dan kelompok metode M ditugaskan ke tipe delegasi D, tugas inferensi tipe adalah menemukan argumen tipe S₁...Sᵥ sehingga ekspresi:

M<S₁...Sᵥ>

menjadi kompatibel (§21.2) dengan D.

Tidak seperti algoritma inferensi jenis untuk panggilan metode generik, dalam hal ini, hanya ada argumen jenis, tidak ada argumen ekspresi. Secara khusus, tidak ada fungsi anonim dan karenanya tidak perlu beberapa fase inferensi.

Sebaliknya, semua Xᵢ dianggap tidak tetap, dan inferensi batas bawah dibuat dari setiap jenis argumen UₑD jenis parameter yang sesuai Tₑ. Jika untuk salah satu Xᵢ tidak ditemukan batasannya, inferensi tipe gagal. Jika tidak, semua Xᵢ ditetapkan ke Sᵢ yang sesuai, yang merupakan hasil dari inferensi jenis.

12.6.3.16 Menemukan jenis ekspresi umum terbaik

Dalam beberapa kasus, jenis umum perlu disimpulkan untuk serangkaian ekspresi. Secara khusus, jenis elemen array yang diketik secara implisit dan jenis pengembalian fungsi anonim dengan badan blok ditemukan dengan cara ini.

Jenis umum terbaik untuk sekumpulan ekspresi E₁...Eᵥ ditentukan sebagai berikut:

  • Variabel jenis baru yang tidak tetapX diperkenalkan.
  • Untuk setiap ekspresi Ei, inferensi jenis output (§12.6.3.8) dilakukan darinya ke X.
  • X diperbaiki (§12.6.3.13), jika memungkinkan, dan jenis yang dihasilkan adalah jenis umum terbaik.
  • Jika tidak, inferensi gagal.

Catatan: Secara intuitif inferensi ini setara dengan memanggil metode void M<X>(X x₁ ... X xᵥ) dengan Eᵢ sebagai argumen dan menyimpulkan X. catatan akhir

12.6.4 Resolusi kelebihan beban

12.6.4.1 Umum

Resolusi kelebihan beban adalah mekanisme pengikatan waktu untuk memilih anggota fungsi terbaik yang akan dipanggil dengan daftar argumen yang diberikan dan sekumpulan anggota fungsi kandidat. Resolusi kelebihan beban memilih anggota fungsi yang akan dipanggil dalam konteks berbeda berikut dalam C#:

  • Pemanggilan metode yang disebut dalam invocation_expression (§12.8.10).
  • Pemanggilan konstruktor instance yang dinamai dalam ekspresi_pembuatan_objek (§12.8.17.2).
  • Pemanggilan aksesor pengindeks melalui element_access (§12.8.12).
  • Pemanggilan operator yang telah ditentukan sebelumnya atau oleh pengguna dalam sebuah ekspresi (§12.4.4 dan §12.4.5).

Masing-masing konteks ini mendefinisikan kumpulan anggota fungsi kandidat dan daftar argumen dengan cara uniknya sendiri. Misalnya, sekumpulan kandidat untuk pemanggilan metode tidak menyertakan metode yang ditandai override (§12.5), dan metode dalam kelas dasar bukan kandidat jika metode apa pun dalam kelas turunan berlaku (§12.8.10.2).

Setelah anggota fungsi kandidat dan daftar argumen diidentifikasi, pemilihan anggota fungsi terbaik sama dalam semua kasus:

  • Pertama, kumpulan anggota fungsi kandidat dikurangi menjadi anggota fungsi yang berlaku sehubungan dengan daftar argumen yang diberikan (§12.6.4.2). Jika himpunan yang dikurangi ini kosong, akan terjadi kesalahan waktu kompilasi.
  • Kemudian, anggota fungsi terbaik dari kandidat fungsi yang berlaku dalam kumpulan tersebut ditemukan. Jika set hanya berisi satu anggota fungsi, maka anggota fungsi tersebut adalah anggota fungsi terbaik. Jika tidak, anggota fungsi terbaik adalah satu anggota fungsi yang lebih baik daripada semua anggota fungsi lainnya sehubungan dengan daftar argumen yang diberikan, asalkan setiap anggota fungsi dibandingkan dengan semua anggota fungsi lain menggunakan aturan dalam §12.6.4.3. Jika tidak ada hanya satu anggota fungsi yang lebih baik daripada semua anggota fungsi lainnya, maka pemanggilan anggota fungsi menjadi ambigu dan kesalahan waktu pengikatan terjadi.

Subklasul berikut menentukan arti yang tepat dari istilah anggota fungsi yang berlaku dan anggota fungsi yang lebih baik.

12.6.4.2 Anggota fungsi yang berlaku

Anggota fungsi dikatakan sebagai anggota fungsi yang berlaku sehubungan dengan daftar argumen A ketika semua hal berikut ini benar:

  • Setiap argumen dalam A sesuai dengan parameter dalam deklarasi anggota fungsi seperti yang dijelaskan dalam §12.6.2.2, paling banyak satu argumen sesuai dengan setiap parameter, dan parameter apa pun yang tidak ada argumen yang sesuai adalah parameter opsional.
  • Untuk setiap argumen dalam A, mode pengiriman parameter dari argumen identik dengan mode pengiriman parameter dari parameter yang bersesuaian, dan
    • untuk parameter nilai atau array parameter, konversi implisit (§10.2) ada dari ekspresi argumen ke tipe parameter yang sesuai, atau
    • untuk parameter referensi atau output, ada konversi identitas antara jenis ekspresi argumen (jika ada) dan jenis parameter yang sesuai, atau
    • untuk parameter input ketika argumen yang sesuai memiliki pengubah in, ada konversi identitas antara jenis ekspresi argumen (jika ada) dan jenis parameter yang sesuai, atau
    • untuk parameter input saat argumen yang sesuai menghilangkan pengubah in, konversi implisit (§10,2) ada dari ekspresi argumen ke jenis parameter yang sesuai.

Untuk anggota fungsi yang menyertakan array parameter, jika anggota fungsi berlaku oleh aturan di atas, dikatakan berlaku dalam bentuk normal . Jika anggota fungsi yang menyertakan array parameter tidak berlaku dalam bentuk normalnya, anggota fungsi mungkin berlaku dalam formulir diperluas:

  • Formulir yang diperluas dibangun dengan mengganti array parameter dalam deklarasi anggota fungsi dengan parameter nilai nol atau lebih dari jenis elemen array parameter sehingga jumlah argumen dalam daftar argumen A cocok dengan jumlah total parameter. Jika A memiliki lebih sedikit argumen daripada jumlah parameter tetap dalam deklarasi anggota fungsi, bentuk anggota fungsi yang diperluas tidak dapat dibangun dan dengan demikian tidak berlaku.
  • Jika tidak, formulir yang diperluas berlaku jika untuk setiap argumen dalam A, salah satu hal berikut ini adalah benar:
    • mode pemindahan parameter dari argumen identik dengan mode pemindahan parameter dari parameter yang sesuai, dan:
      • untuk parameter nilai tetap atau parameter nilai yang dibuat oleh ekspansi, konversi implisit (§10,2) ada dari ekspresi argumen ke jenis parameter yang sesuai; atau
      • untuk parameter dengan acuan, tipe ekspresi argumen identik dengan tipe parameter yang sesuai.
    • modus pengoperan parameter dari argument adalah nilai, dan modus pengoperan parameter yang sesuai adalah input, dan terdapat konversi implisit (§10.2) dari ekspresi argument ke tipe parameter yang bersesuaian.

Ketika konversi implisit dari jenis argumen ke jenis parameter parameter input adalah konversi implisit dinamis (§10.2.10), hasilnya tidak ditentukan.

Contoh: Diberikan deklarasi dan pemanggilan metode berikut:

public static void M1(int p1) { ... }
public static void M1(in int p1) { ... }
public static void M2(in int p1) { ... }
public static void Test()
{
    int i = 10; uint ui = 34U;

    M1(in i);   // M1(in int) is applicable
    M1(in ui);  // no exact type match, so M1(in int) is not applicable
    M1(i);      // M1(int) and M1(in int) are applicable
    M1(i + 5);  // M1(int) and M1(in int) are applicable
    M1(100u);   // no implicit conversion exists, so M1(int) is not applicable

    M2(in i);   // M2(in int) is applicable
    M2(i);      // M2(in int) is applicable
    M2(i + 5);  // M2(in int) is applicable
}

contoh akhir

  • Metode statis hanya berlaku jika kelompok metode dihasilkan dari simple_name atau member_access melalui tipe.
  • Metode instans hanya berlaku jika grup metode berasal dari simple_name, member_access melalui variabel atau nilai, atau base_access.
    • Jika grup metode dihasilkan dari suatu simple_name, metode instans hanya dapat berlaku jika akses this diizinkan §12.8.14.
  • Ketika kelompok metode dihasilkan dari member_access yang dapat dilakukan melalui sebuah instans atau tipe seperti yang dijelaskan dalam §12.8.7.2, baik metode instans maupun metode statis dapat diterapkan.
  • Metode generik yang argumen jenisnya (ditentukan secara eksplisit atau disimpulkan) tidak semua memenuhi batasannya tidak berlaku.
  • Dalam konteks konversi grup metode, akan ada konversi identitas (§10.2.2) atau konversi referensi implisit (§10,2,8) dari jenis pengembalian metode ke jenis pengembalian delegasi. Jika tidak, metode kandidat tidak berlaku.

12.6.4.3 Anggota fungsi yang lebih baik

Untuk tujuan menentukan anggota fungsi yang lebih baik, daftar argumen yang dilucuti A dibangun hanya berisi ekspresi argumen itu sendiri dalam urutan muncul dalam daftar argumen asli, dan meninggalkan argumen out atau ref apa pun.

Daftar parameter untuk setiap anggota fungsi kandidat dibangun dengan cara berikut:

  • Formulir yang diperluas digunakan jika anggota fungsi hanya berlaku dalam formulir yang diperluas.
  • Parameter opsional tanpa argumen terkait dihapus dari daftar parameter
  • Parameter referensi dan output dihapus dari daftar parameter
  • Parameter diurutkan ulang sehingga terjadi pada posisi yang sama dengan argumen yang sesuai dalam daftar argumen.

Mengingat daftar argumen A dengan sekumpulan ekspresi argumen {E₁, E₂, ..., Eᵥ} dan dua anggota fungsi yang berlaku Mᵥ dan Mₓ dengan jenis parameter {P₁, P₂, ..., Pᵥ} dan {Q₁, Q₂, ..., Qᵥ}, Mᵥ didefinisikan menjadi anggota fungsi lebih baik daripada Mₓ jika

  • untuk setiap argumen, konversi implisit dari Eᵥ ke Qᵥ tidak lebih baik daripada konversi implisit dari Eᵥ ke Pᵥ, dan
  • untuk setidaknya satu argumen, konversi dari Eᵥ ke Pᵥ lebih baik daripada konversi dari Eᵥ ke Qᵥ.

Jika urutan jenis parameter {P₁, P₂, ..., Pᵥ} dan {Q₁, Q₂, ..., Qᵥ} setara (yaitu, setiap Pᵢ memiliki konversi identitas ke Qᵢyang sesuai ), aturan pemecahan ikatan berikut diterapkan, untuk menentukan anggota fungsi yang lebih baik.

  • Jika Mᵢ adalah metode non-generik dan Mₑ adalah metode generik, maka Mᵢ lebih baik daripada Mₑ.
  • Jika tidak, jika Mᵢ berlaku dalam bentuk normalnya dan Mₑ memiliki array param dan hanya berlaku dalam bentuknya yang diperluas, maka Mᵢ lebih baik daripada Mₑ.
  • Jika tidak, jika kedua metode memiliki array param dan hanya berlaku dalam bentuk yang diperluas, dan jika array param Mᵢ memiliki lebih sedikit elemen daripada array param Mₑ, maka Mᵢ lebih baik daripada Mₑ.
  • Jika tidak, jika Mᵥ memiliki jenis parameter yang lebih spesifik daripada Mₓ, maka Mᵥ lebih baik daripada Mₓ. Biarkan {R1, R2, ..., Rn} dan {S1, S2, ..., Sn} mewakili jenis parameter Mᵥ dan Mₓyang tidak terinstansiasi dan tidak diperluas. jenis parameter Mᵥlebih spesifik daripada Mₓjika, untuk setiap parameter, Rx tidak kurang spesifik daripada Sx, dan, setidaknya untuk satu parameter, Rx lebih spesifik daripada Sx:
    • Parameter jenis kurang spesifik daripada parameter non-jenis.
    • Secara rekursif, jenis yang dibangun lebih spesifik daripada jenis lain yang dibuat (dengan jumlah argumen jenis yang sama) jika setidaknya satu argumen jenis lebih spesifik dan tidak ada argumen jenis yang kurang spesifik daripada argumen jenis yang sesuai di argumen jenis lainnya.
    • Jenis array lebih spesifik daripada jenis array lain (dengan jumlah dimensi yang sama) jika jenis elemen pertama lebih spesifik daripada jenis elemen kedua.
  • Jika tidak, jika satu anggota adalah operator yang tidak diangkat dan yang lain adalah operator yang diangkat, yang tidak diangkat lebih baik.
  • Jika tidak ada anggota fungsi yang ditemukan lebih baik, dan semua parameter Mᵥ memiliki argumen yang sesuai sedangkan argumen default perlu diganti dengan setidaknya satu parameter opsional dalam Mₓ, maka Mᵥ lebih baik daripada Mₓ.
  • Jika untuk setidaknya satu parameter menggunakan pilihan penerusan parameter yang lebih baik (§12.6.4.4) daripada parameter yang sesuai di dan tidak ada parameter dalam yang menggunakan pilihan penerusan parameter yang lebih baik daripada , lebih baik daripada .
  • Jika tidak, tidak ada anggota fungsi yang lebih unggul.

12.6.4.4 Metode pengiriman parameter yang lebih baik

Diizinkan untuk memiliki parameter yang berbeda dalam dua metode yang di-overload hanya dengan mode pengiriman parameter, asalkan salah satu dari dua parameter memiliki mode pengiriman nilai, sebagai berikut:

public static void M1(int p1) { ... }
public static void M1(in int p1) { ... }

Diberikan int i = 10;, menurut §12.6.4.2, panggilan M1(i) dan M1(i + 5) mengakibatkan kedua overload tersebut dapat diterapkan. Dalam kasus seperti itu, metode dengan mode penyampaian parameter menggunakan nilai adalah pilihan mode penyampaian parameter yang lebih baik.

Catatan: Tidak perlu ada pilihan untuk argumen mode pengoperan input, output, atau referensi, karena argumen tersebut hanya cocok dengan mode pengoperan parameter yang sama persis. catatan akhir

12.6.4.5 Konversi yang lebih baik dari ekspresi

Diberikan konversi implisit C₁ yang mengonversi dari ekspresi E ke jenis T₁, dan konversi implisit C₂ yang mengonversi dari ekspresi E ke jenis T₂, C₁ adalah konversi yang lebih baik daripada C₂ jika salah satu kondisi berikut terpenuhi:

  • E sama persis dengan T₁ dan E tidak sama persis dengan T₂ (§12.6.4.6)
  • E sama persis dengan T₁ dan T₂, dan T₁ adalah target konversi yang lebih baik daripada T₂ (§12.6.4.7)
  • E adalah grup metode (§12.2), T₁ kompatibel (§21,4) dengan metode terbaik tunggal dari grup metode untuk konversi C₁, dan T₂ tidak kompatibel dengan metode terbaik tunggal dari grup metode untuk konversi C₂

12.6.4.6 Ekspresi yang cocok persis

Diberikan ekspresi E dan jenis T, Esama persis denganT jika salah satu ketentuan berikut terpenuhi:

  • E memiliki jenis S, dan konversi identitas ada dari S ke T
  • E adalah fungsi anonim, T adalah jenis delegasi D atau jenis pohon ekspresi Expression<D> dan salah satu dari berikut ini berlaku:
    • Jenis X pengembalian yang disimpulkan ada untuk E dalam konteks daftar D parameter (§12.6.3.13), dan konversi identitas ada dari X ke jenis pengembalian D
    • E adalah lambda async tanpa nilai pengembalian, dan D memiliki jenis pengembalian yang merupakan «TaskType» non-generik
    • Baik E bersifat non-asinkron dan D memiliki tipe pengembalian Y atau E bersifat asinkron dan D memiliki tipe pengembalian «TaskType»<Y>(§15.14.1), dan salah satu yang berikut ini berlaku:
      • Isi E adalah ekspresi yang sama persis dengan Y
      • Isi E adalah blok di mana setiap statement 'return' menghasilkan ekspresi yang persis sama dengan Y

12.6.4.7 Target konversi yang lebih baik

Diberikan dua jenis T₁ dan T₂, T₁ adalah target konversi yang lebih baik daripada T₂ jika salah satu dari berikut ini berlaku:

  • Konversi implisit dari T₁ ke T₂ ada dan tidak ada konversi implisit dari T₂ ke T₁ ada
  • T₁ adalah «TaskType»<S₁>(§15.14.1), T₂ adalah «TaskType»<S₂>, dan S₁ merupakan target konversi yang lebih baik daripada S₂
  • T₁ adalah «TaskType»<S₁>(§15.14.1), T₂ adalah «TaskType»<S₂>, dan T₁ lebih khusus daripada T₂
  • T₁ S₁ atau S₁? di mana S₁ adalah jenis integral yang ditandatangani, dan T₂S₂ atau S₂? di mana S₂ adalah jenis integral yang tidak ditandatangani. Khusus:
    • S₁ adalah sbyte dan S₂ adalah byte, ushort, uint, atau ulong
    • S₁ short dan S₂ushort, uint, atau ulong
    • S₁ adalah int dan S₂ adalah uint, atau ulong
    • S₁ adalah long dan S₂ adalah ulong

12.6.4.8 Kelebihan beban di kelas generik

Catatan: Meskipun tanda tangan seperti yang dinyatakan harus unik (§8,6), ada kemungkinan bahwa penggantian argumen jenis menghasilkan tanda tangan yang identik. Dalam situasi seperti itu, resolusi kelebihan beban akan memilih yang paling spesifik (§12.6.4.3) dari tanda tangan asli (sebelum penggantian argumen jenis), jika ada, dan jika tidak, laporkan kesalahan. catatan akhir

Contoh: Contoh berikut menunjukkan kelebihan beban yang valid dan tidak valid sesuai dengan aturan ini:

public interface I1<T> { ... }
public interface I2<T> { ... }

public abstract class G1<U>
{
    public abstract int F1(U u);           // Overload resolution for G<int>.F1
    public abstract int F1(int i);         // will pick non-generic

    public abstract void F2(I1<U> a);      // Valid overload
    public abstract void F2(I2<U> a);
}

abstract class G2<U,V>
{
    public abstract void F3(U u, V v);     // Valid, but overload resolution for
    public abstract void F3(V v, U u);     // G2<int,int>.F3 will fail

    public abstract void F4(U u, I1<V> v); // Valid, but overload resolution for
    public abstract void F4(I1<V> v, U u); // G2<I1<int>,int>.F4 will fail

    public abstract void F5(U u1, I1<V> v2);   // Valid overload
    public abstract void F5(V v1, U u2);

    public abstract void F6(ref U u);      // Valid overload
    public abstract void F6(out V v);
}

contoh akhir

12.6.5 Pemeriksaan waktu kompilasi pemanggilan anggota dinamis

Meskipun penyelesaian kelebihan beban dari operasi yang terikat secara dinamis dilakukan pada saat run-time, terkadang mungkin untuk mengetahui daftar anggota fungsi dari mana kelebihan beban akan dipilih pada saat kompilasi.

  • Untuk pemanggilan delegate (§12.8.10.4), daftar tersebut adalah anggota fungsi tunggal dengan daftar parameter yang sama dengan delegate_type dari pemanggilan tersebut.
  • Untuk pemanggilan metode (§12.8.10.2) pada jenis, atau pada nilai yang jenis statisnya tidak dinamis, kumpulan metode yang dapat diakses dalam grup metode dikenal pada waktu kompilasi.
  • Untuk ekspresi pembuatan objek (§12.8.17.2) kumpulan konstruktor yang dapat diakses dalam jenis diketahui pada waktu kompilasi.
  • Untuk akses pengindeks (§12.8.12.4) kumpulan pengindeks yang dapat diakses di penerima diketahui pada waktu kompilasi.

Dalam kasus ini, pemeriksaan pada waktu kompilasi terbatas dilakukan pada setiap anggota dalam set anggota fungsi yang telah dikenal, guna memastikan bahwa tidak akan pernah dipanggil pada saat run-time. Untuk setiap anggota fungsi F parameter dan daftar argumen yang dimodifikasi dibuat:

  • Pertama, jika F adalah metode generik dan argumen jenis disediakan, maka argumen tersebut digantikan untuk parameter jenis dalam daftar parameter. Namun, jika argumen jenis tidak disediakan, tidak ada penggantian tersebut yang terjadi.
  • Kemudian, parameter apa pun yang tipenya terbuka (yaitu, berisi parameter tipe; lihat §8.4.3) diabaikan, bersama dengan argumen yang bersesuaian.

Agar F lulus pemeriksaan, semua hal berikut harus dipenuhi:

  • Daftar parameter yang dimodifikasi untuk F berlaku untuk daftar argumen yang dimodifikasi dalam hal §12.6.4.2.
  • Semua jenis yang dibangun dalam daftar parameter yang dimodifikasi memenuhi batasannya (§8.4.5).
  • Jika parameter jenis F diganti pada langkah di atas, batasannya terpenuhi.
  • Jika F adalah metode statis, grup metode tidak akan dihasilkan oleh member_access yang penerimanya dikenal pada waktu kompilasi sebagai variabel atau nilai.
  • Jika F adalah metode instans, grup metode tidak boleh berasal dari member_access yang penerimanya diketahui pada waktu kompilasi sebagai tipe.

Jika tidak ada kandidat yang lolos tes ini, kesalahan waktu kompilasi terjadi.

12.6.6 Pemanggilan anggota fungsi

12.6.6.1 Umum

Subklasul ini menjelaskan proses yang terjadi pada run-time untuk memanggil anggota fungsi tertentu. Diasumsikan proses waktu pengikatan telah menentukan anggota tertentu untuk dipanggil, mungkin dengan menerapkan penyelesaian kelebihan beban pada sekumpulan kandidat anggota fungsi.

Untuk tujuan menggambarkan proses pemanggilan, anggota fungsi dibagi menjadi dua kategori:

  • Anggota fungsi statis. Ini adalah metode statis, pengaktor properti statis, dan operator yang ditentukan pengguna. Anggota fungsi statis selalu non-virtual.
  • Anggota fungsi instans. Ini adalah metode instans, konstruktor instans, pengakses properti instans, dan pengakses pengindeks. Anggota fungsi objek adalah non-virtual atau virtual, dan selalu dipanggil pada objek tertentu. Instans dihasilkan oleh suatu ekspresi instance, dan dapat diakses dalam anggota fungsi sebagai this (§12.8.14). Untuk konstruktor instans, ekspresi instans dipandang sebagai objek yang baru dialokasikan.

Pemrosesan run-time dari pemanggilan anggota fungsi terdiri dari langkah-langkah berikut, di mana M adalah anggota fungsi dan, jika M adalah anggota instans, E adalah ekspresi instans:

  • Jika M adalah anggota fungsi statis:
    • Daftar argumen dievaluasi seperti yang dijelaskan dalam §12.6.2.
    • M diaktifkan.
  • Jika tidak, jika jenisnya E adalah jenis Vnilai , dan M dinyatakan atau ditimpa dalam V:
    • E dievaluasi. Jika evaluasi ini menyebabkan pengecualian, maka tidak ada langkah lebih lanjut yang dijalankan. Untuk konstruktor instans, evaluasi ini terdiri dari mengalokasikan penyimpanan (biasanya dari tumpukan eksekusi) untuk objek baru. Dalam hal ini E diklasifikasikan sebagai variabel.
    • Jika E tidak diklasifikasikan sebagai variabel, atau jika V bukan jenis struct baca-saja (§16.2.2) dan M bukan anggota fungsi readonly (§16.4.12), dan E merupakan salah satu dari:
      • parameter masuk (§15.6.2.3.2), atau
      • bidang readonly (§15.5.3), atau
      • readonly variabel referensi atau pengembalian (§9,7), lalu variabel lokal sementara dari Ejenis 's dibuat dan nilai E ditetapkan ke variabel tersebut. E kemudian diklasifikasi ulang sebagai referensi ke variabel lokal sementara tersebut. Variabel sementara dapat diakses sebagai this dalam M, tetapi tidak dengan cara lain. Dengan demikian, hanya ketika E dapat ditulis adalah mungkin bagi pemanggil untuk mengamati perubahan yang M buat untuk this.
    • Daftar argumen dievaluasi seperti yang dijelaskan dalam §12.6.2.
    • M diaktifkan. Variabel yang dirujuk oleh E menjadi variabel yang dirujuk oleh this.
  • Sebaliknya:
    • E dievaluasi. Jika evaluasi ini menyebabkan pengecualian, maka tidak ada langkah lebih lanjut yang dijalankan.
    • Daftar argumen dievaluasi seperti yang dijelaskan dalam §12.6.2.
    • Jika jenis E adalah value_type, konversi pembungkusan (§10.2.9) dilakukan untuk mengonversi E ke class_type, dan E dianggap memiliki class_type tersebut dalam langkah-langkah berikut. Jika value_type adalah enum_type, maka class_type adalah System.Enum;; jika tidak, maka System.ValueType.
    • Nilai E diperiksa agar valid. Jika nilai E null, System.NullReferenceException akan dilemparkan dan tidak ada langkah lebih lanjut yang dijalankan.
    • Implementasi anggota fungsi yang akan dipanggil ditentukan:
      • Jika tipe pengikatan waktu E adalah antarmuka, anggota fungsi yang akan dipanggil adalah implementasi M yang disediakan oleh tipe waktu proses instans yang dirujuk oleh E. Anggota fungsi ini ditentukan dengan menerapkan aturan pemetaan antarmuka (§19.6.5) untuk menentukan implementasi M yang disediakan oleh jenis run-time instans yang dirujuk oleh E.
      • Jika M adalah anggota fungsi virtual, maka fungsi yang akan dipanggil adalah implementasi dari M yang disediakan oleh tipe run-time dari instans yang dirujuk oleh E. Anggota fungsi ini ditentukan dengan menerapkan aturan untuk menentukan implementasi yang paling turunan (§15,6,4) dari M sehubungan dengan jenis run-time instans yang dirujuk oleh E.
      • Jika tidak, M adalah fungsi anggota non-virtual, dan fungsi anggota yang akan dipanggil adalah M itu sendiri.
    • Implementasi fungsi anggota yang ditentukan pada langkah di atas dipanggil. Objek yang dirujuk oleh E menjadi objek yang dirujuk oleh ini.

Catatan:§12.2 mengklasifikasikan akses properti sebagai memanggil anggota fungsi yang sesuai, baik get aksesor atau set aksesor. Proses di atas diikuti untuk memanggil aksesor tersebut. catatan akhir

Hasil pemanggilan konstruktor instans (§12.8.17.2) adalah nilai yang dibuat. Hasil pemanggilan anggota fungsi lain adalah nilai, jika ada, dikembalikan (§13,10,5) dari isinya.

12.6.6.2 Pemanggilan pada instans yang dibungkus

Anggota fungsi yang diterapkan dalam value_type dapat dipanggil melalui instans kotak dari value_type tersebut dalam situasi berikut:

  • Ketika anggota fungsi adalah penggantian metode yang diwarisi dari tipe kelas dan dipanggil melalui ekspresi instans tipe kelas tersebut.

    Catatan: class_type akan selalu menjadi salah satu System.Object, System.ValueType atau System.Enumcatatan akhir

  • Ketika anggota fungsi adalah implementasi dari anggota fungsi antarmuka dan dipanggil melalui ekspresi instance interface_type.
  • Ketika anggota fungsi dipanggil melalui delegasi.

Dalam situasi ini, instans kotak dianggap berisi variabel value_type, dan variabel ini menjadi variabel yang direferensikan oleh ini dalam pemanggilan anggota fungsi.

Catatan: Secara khusus, ini berarti bahwa ketika anggota fungsi dipanggil pada instans kotak, dimungkinkan bagi anggota fungsi untuk memodifikasi nilai yang terkandung dalam instans kotak. catatan akhir

12.7 Dekonstruksi

Dekonstruksi adalah proses dimana suatu ekspresi diubah menjadi sekumpulan ekspresi individu. Dekonstruksi digunakan ketika target penugasan sederhana adalah ekspresi tuple, dalam rangka mendapatkan nilai yang akan diberikan kepada setiap elemen tuple tersebut.

Sebuah ekspresi E adalah yang didekonstruksi menjadi ekspresi tuple dengan n elemen dengan cara berikut:

  • Jika E adalah ekspresi tuple dengan elemen n, hasil dekonstruksi adalah ekspresi E itu sendiri.
  • Selain itu, jika E memiliki tipe tuple (T1, ..., Tn) dengan n elemen, maka E dievaluasi menjadi variabel sementara __v, dan hasil dari dekonstruksi adalah ekspresi (__v.Item1, ..., __v.Itemn).
  • Jika tidak, apabila ekspresi E.Deconstruct(out var __v1, ..., out var __vn) diselesaikan menjadi instans unik atau metode ekstensi pada waktu kompilasi, maka ekspresi tersebut dievaluasi, dan hasil dekonstruksi adalah ekspresi (__v1, ..., __vn). Metode seperti itu disebut dengan nama dekonstruktor .
  • Jika tidak, E tidak dapat didekonstruksi.

Di sini, __v dan __v1, ..., __vn mengacu pada variabel sementara yang tidak terlihat dan tidak dapat diakses.

Catatan: Ekspresi jenis dynamic tidak dapat didekonstruksi. catatan akhir

12.8 Ekspresi utama

12.8.1 Umum

Ekspresi utama mencakup bentuk ekspresi yang paling sederhana.

primary_expression
    : literal
    | interpolated_string_expression
    | simple_name
    | parenthesized_expression
    | tuple_expression
    | member_access
    | null_conditional_member_access
    | invocation_expression
    | element_access
    | null_conditional_element_access
    | this_access
    | base_access
    | post_increment_expression
    | post_decrement_expression
    | null_forgiving_expression
    | array_creation_expression
    | object_creation_expression
    | delegate_creation_expression
    | anonymous_object_creation_expression
    | typeof_expression
    | sizeof_expression
    | checked_expression
    | unchecked_expression
    | default_value_expression
    | nameof_expression    
    | anonymous_method_expression
    | pointer_member_access     // unsafe code support
    | pointer_element_access    // unsafe code support
    | stackalloc_expression
    ;

Catatan: Aturan tata bahasa ini tidak siap untuk ANTLR karena merupakan bagian dari sekumpulan aturan rekursif kiri yang saling terkait (primary_expression, member_access, invocation_expression, element_access, post_increment_expression, post_decrement_expression, null_forgiving_expression, pointer_member_access, dan pointer_element_access) yang tidak dapat diproses oleh ANTLR. Teknik-teknik standar dapat digunakan untuk mengubah gramatika guna menghapus rekursi kiri yang saling terkait. Standar ini tidak melakukan ini karena tidak semua strategi penguraian memerlukannya (misalnya pengurai LALR tidak akan) dan melakukannya akan mengaburkan struktur dan deskripsi. catatan akhir

pointer_member_access (§24.6.3) dan pointer_element_access (§24.6.4) hanya tersedia dalam kode yang tidak aman (§24).

12.8.2 Literal

primary_expression yang terdiri dari literal (§6.4.5) diklasifikasikan sebagai nilai.

12.8.3 Ekspresi string terinterpolasi

interpolated_string_expression terdiri dari $, $@, atau @$, segera diikuti oleh teks dalam karakter ". Dalam teks yang dikutip ada nol atau lebih interpolasi dibatasi oleh karakter { dan }, yang masing-masing mencakup ekspresi dan spesifikasi pemformatan opsional.

Ekspresi string terinterpolasi memiliki dua bentuk; reguler (interpolated_regular_string_expression) dan verbatim (interpolated_verbatim_string_expression); yang secara leksikal mirip dengan, tetapi berbeda secara semantik, dua bentuk literal string (§6.4.5.6).

interpolated_string_expression
    : interpolated_regular_string_expression
    | interpolated_verbatim_string_expression
    ;

// interpolated regular string expressions

interpolated_regular_string_expression
    : Interpolated_Regular_String_Start Interpolated_Regular_String_Mid?
      ('{' regular_interpolation '}' Interpolated_Regular_String_Mid?)*
      Interpolated_Regular_String_End
    ;

regular_interpolation
    : expression (',' interpolation_minimum_width)?
      Regular_Interpolation_Format?
    ;

interpolation_minimum_width
    : constant_expression
    ;

Interpolated_Regular_String_Start
    : '$"'
    ;

// the following three lexical rules are context sensitive, see details below

Interpolated_Regular_String_Mid
    : Interpolated_Regular_String_Element+
    ;

Regular_Interpolation_Format
    : ':' Interpolated_Regular_String_Element+
    ;

Interpolated_Regular_String_End
    : '"'
    ;

fragment Interpolated_Regular_String_Element
    : Interpolated_Regular_String_Character
    | Simple_Escape_Sequence
    | Hexadecimal_Escape_Sequence
    | Unicode_Escape_Sequence
    | Open_Brace_Escape_Sequence
    | Close_Brace_Escape_Sequence
    ;

fragment Interpolated_Regular_String_Character
    // Any character except " (U+0022), \\ (U+005C),
    // { (U+007B), } (U+007D), and New_Line_Character.
    : ~["\\{}\u000D\u000A\u0085\u2028\u2029]
    ;

// interpolated verbatim string expressions

interpolated_verbatim_string_expression
    : Interpolated_Verbatim_String_Start Interpolated_Verbatim_String_Mid?
      ('{' verbatim_interpolation '}' Interpolated_Verbatim_String_Mid?)*
      Interpolated_Verbatim_String_End
    ;

verbatim_interpolation
    : expression (',' interpolation_minimum_width)?
      Verbatim_Interpolation_Format?
    ;

Interpolated_Verbatim_String_Start
    : '$@"'
    | '@$"'
    ;

// the following three lexical rules are context sensitive, see details below

Interpolated_Verbatim_String_Mid
    : Interpolated_Verbatim_String_Element+
    ;

Verbatim_Interpolation_Format
    : ':' Interpolated_Verbatim_String_Element+
    ;

Interpolated_Verbatim_String_End
    : '"'
    ;

fragment Interpolated_Verbatim_String_Element
    : Interpolated_Verbatim_String_Character
    | Quote_Escape_Sequence
    | Open_Brace_Escape_Sequence
    | Close_Brace_Escape_Sequence
    ;

fragment Interpolated_Verbatim_String_Character
    : ~["{}]    // Any character except " (U+0022), { (U+007B) and } (U+007D)
    ;

// lexical fragments used by both regular and verbatim interpolated strings

fragment Open_Brace_Escape_Sequence
    : '{{'
    ;

fragment Close_Brace_Escape_Sequence
    : '}}'
    ;

Enam dari aturan leksikal yang ditentukan di atas adalah kontekstual-sensitif sebagai berikut:

Aturan Persyaratan Kontekstual
Interpolated_Regular_String_Mid Hanya dikenali setelah Interpolated_Regular_String_Start, antara interpolasi berikut, dan sebelum Interpolated_Regular_String_Endyang sesuai.
Regular_Interpolation_Format Hanya dikenali dalam regular_interpolation dan ketika titik dua di awal (:) tidak bersarang dalam jenis tanda kurung apapun (tanda kurung/kurung kurawal/persegi).
Interpolated_Regular_String_End Hanya dikenali setelah Interpolated_Regular_String_Start dan hanya jika ada token intervensi yang merupakan Interpolated_Regular_String_Midatau token yang dapat menjadi bagian dari regular_interpolation, termasuk token untuk interpolated_regular_string_expressionyang ada dalam interpolasi tersebut.
Interpolated_Verbatim_String_EndVerbatim_Interpolation_Format Interpolated_Verbatim_String_Mid Pengenalan ketiga aturan ini mengikuti aturan yang sesuai di atas di mana setiap aturan tata bahasa reguler yang disebutkan digantikan oleh aturan verbatim yang sesuai.

Catatan: Aturan di atas peka konteks karena definisinya tumpang tindih dengan token lain dalam bahasa. catatan akhir

Catatan: Tata bahasa di atas tidak siap untuk ANTLR karena aturan leksikal sensitif konteks. Seperti halnya generator lexer ANTLR lainnya mendukung aturan leksikal sensitif konteks, misalnya menggunakan mode leksikal , tetapi ini adalah detail implementasi dan oleh karena itu bukan bagian dari spesifikasi ini. catatan akhir

interpolated_string_expression diklasifikasikan sebagai nilai. Jika segera dikonversi ke System.IFormattable atau System.FormattableString dengan konversi string terinterpolasi implisit (§10,2,5), ekspresi string terinterpolasi memiliki jenis tersebut. Jika tidak, ia memiliki tipe string.

Catatan: Perbedaan antara kemungkinan jenis interpolated_string_expression dapat ditentukan dari dokumentasi untuk System.String (§C.2) dan System.FormattableString (§C.3). catatan akhir

Arti interpolasi, baik regular_interpolation maupun verbatim_interpolation, adalah memformat nilai ekspresi sebagai string sesuai dengan format yang ditentukan oleh Regular_Interpolation_Format atau Verbatim_Interpolation_Format, atau sesuai dengan format default untuk jenis ekspresi . String yang diformat kemudian dimodifikasi oleh interpolation_minimum_width, jika ada, untuk menghasilkan string akhir yang akan diinterpolasi ke dalam interpolated_string_expression.

Catatan: Bagaimana format default untuk tipe ditentukan dijelaskan dalam dokumentasi untuk System.String (§C.2) dan System.FormattableString (§C.3). Deskripsi format standar, yang identik untuk Regular_Interpolation_Format dan Verbatim_Interpolation_Format, dapat ditemukan dalam dokumentasi untuk System.IFormattable (§C.4) dan di jenis lain di pustaka standar (§C). catatan akhir

Dalam interpolation_minimum_width, constant_expression memiliki konversi implisit ke int. Biarkan lebar bidang menjadi nilai absolut dari constant_expression ini dan perataan menjadi tanda (positif atau negatif) dari nilai constant_expressionini :

  • Jika nilai lebar bidang kurang dari atau sama dengan panjang string yang diformat, string yang diformat tidak dimodifikasi.
  • Jika tidak, string yang diformat diisi dengan karakter spasi putih sehingga panjangnya sama dengan lebar bidang:
    • Jika perataan positif, string yang diformat diratakan ke kanan dengan menambahkan padding di depan.
    • Jika tidak, itu diselaraskan kiri dengan menambahkan padding.

Arti keseluruhan dari interpolated_string_expression, termasuk pemformatan dan pengisi interpolasi di atas, didefinisikan oleh konversi ekspresi ke pemanggilan metode: jika jenis ekspresi System.IFormattable atau System.FormattableString metode tersebut System.Runtime.CompilerServices.FormattableStringFactory.Create (§C.3) yang mengembalikan nilai jenis System.FormattableString; jika tidak, jenisnya harus string dan metodenya string.Format (§C.2) yang mengembalikan nilai jenis string.

Dalam kedua kasus, daftar argumen panggilan terdiri dari string format harfiah dengan spesifikasi format untuk setiap interpolasi, dan sebuah argumen untuk setiap ekspresi yang sesuai dengan spesifikasi format tersebut.

String format literal ditulis sebagai berikut, di mana N adalah jumlah interpolasi dalam interpolated_string_expression. Bentuk string literal terdiri dari, secara berurutan:

  • Karakter ini: Interpolated_Regular_String_Start atau Interpolated_Verbatim_String_Start
  • Karakter-karakter dari Interpolated_Regular_String_Mid atau Interpolated_Verbatim_String_Mid, jika ada
  • Kemudian jika N ≥ 1 untuk setiap angka I dari 0 hingga N-1:
    • Spesifikasi tempat penampung:
      • Karakter kurung kurawal kiri ({)
      • Representasi desimal dari I
      • Kemudian, jika regular_interpolation atau verbatim_interpolation yang sesuai memiliki interpolation_minimum_width, koma (,) diikuti dengan representasi desimal dari nilai constant_expression
      • Karakter dari Regular_Interpolation_Format atau Verbatim_Interpolation_Format, jika ada, yang dari regular_interpolation atau verbatim_interpolation yang sesuai
      • Kurung kurawal kanan (})
    • Karakter Interpolated_Regular_String_Mid atau Interpolated_Verbatim_String_Mid akan langsung mengikuti interpolasi yang sesuai, jika ada
  • Akhirnya, karakter dari Interpolated_Regular_String_End atau Interpolated_Verbatim_String_End.

Argumen-argumen berikut adalah ekspresi-ekspresi dandari interpolasi, jika ada, secara berurutan.

Saat interpolated_string_expression berisi beberapa interpolasi, ekspresi dalam interpolasi tersebut dievaluasi dalam urutan tekstual dari kiri ke kanan.

Contoh:

Contoh ini menggunakan fitur spesifikasi format berikut:

  • spesifikasi format X yang memformat bilangan bulat sebagai heksadesimal dalam huruf besar,
  • format default untuk nilai string adalah nilai itu sendiri,
  • nilai penyelarasan positif yang rata kanan dalam lebar bidang minimum yang ditentukan,
  • nilai perataan negatif yang diratakan ke kiri dalam lebar bidang minimum yang ditentukan,
  • konstanta yang telah ditetapkan untuk interpolation_minimum_width, dan
  • {{ dan }} diformat sebagai { dan }.

Diberikan:

string text = "red";
int number = 14;
const int width = -4;

Kemudian:

Ekspresi String Terinterpolasi Arti Setara Sebagai string Nilai
$"{text}" string.Format("{0}", text) "red"
$"{{text}}" string.Format("{{text}}) "{text}"
$"{ text , 4 }" string.Format("{0,4}", text) " red"
$"{ text , width }" string.Format("{0,-4}", text) "red "
$"{number:X}" string.Format("{0:X}", number) "E"
$"{text + '?'} {number % 3}" string.Format("{0} {1}", text + '?', number % 3) "red? 2"
$"{text + $"[{number}]"}" string.Format("{0}", text + string.Format("[{0}]", number)) "red[14]"
$"{(number==0?"Zero":"Non-zero")}" string.Format("{0}", (number==0?"Zero":"Non-zero")) "Non-zero"

contoh akhir

12.8.4 Nama sederhana

simple_name terdiri dari identifikator yang opsional diikuti oleh daftar argumen jenis:

simple_name
    : identifier type_argument_list?
    ;

simple_name adalah salah satu bentuk I atau bentuk I<A₁, ..., Aₑ>, dengan I sebagai identifier tunggal dan I<A₁, ..., Aₑ> sebagai type_argument_listopsional. Ketika tidak ada type_argument_list yang ditentukan, pertimbangkan e menjadi nol. simple_name dievaluasi dan diklasifikasikan sebagai berikut:

  • Jika e nol dan simple_name muncul dalam ruang deklarasi variabel lokal (§7,3) yang secara langsung berisi variabel lokal, parameter atau konstanta dengan nama I, maka simple_name mengacu pada variabel, parameter, atau konstanta lokal tersebut dan diklasifikasikan sebagai variabel atau nilai.
  • Jika e nol dan simple_name muncul dalam deklarasi metode generik tetapi berada di luar atribut dari method_declaration, dan jika deklarasi tersebut menyertakan parameter jenis dengan nama I, maka simple_name mengacu pada parameter jenis tersebut.
  • Jika tidak, untuk setiap jenis instans T (§15.3.2), dimulai dengan jenis instans dari deklarasi jenis yang segera diapit dan dilanjutkan dengan jenis instans dari setiap kelas penutup atau deklarasi struktur (jika ada):
    • Jika e nol dan deklarasi T menyertakan parameter jenis dengan nama I, maka simple_name mengacu pada parameter jenis tersebut.
    • Jika tidak, jika pencarian anggota (§12.5) I di T dengan argumen jenis e menghasilkan kecocokan:
      • Jika T adalah tipe instans dari kelas atau tipe struct yang langsung mengelilingi dan pencarian mengidentifikasi satu atau beberapa metode, hasilnya adalah kelompok metode dengan ekspresi instans yang terasosiasi dengan this. Jika daftar argumen jenis ditentukan, itu digunakan dalam memanggil metode generik (§12.8.10.2).
      • Jika tidak, jika adalah jenis instans dari kelas atau jenis struct yang langsung melampirkan, jika pencarian mengidentifikasi anggota instans, dan jika referensi terjadi dalam blok konstruktor instans, metode instans, atau pengakses instans (§12.2.1), hasilnya sama dengan pengaksesan anggota (§12.8.7) dari formulir . Ini hanya dapat terjadi ketika e adalah nol.
      • Jika tidak, hasilnya sama dengan akses anggota (§12.8.7) dari bentuk T.I atau T.I<A₁, ..., Aₑ>.
  • Jika tidak, untuk setiap namespace N, dimulai dengan namespace tempat simple_name terjadi, berlanjut dengan setiap namespace pembungkus (jika ada), dan diakhiri dengan namespace global, langkah-langkah berikut dievaluasi hingga entitas ditemukan.
    • Jika e adalah nol dan I adalah nama namespace layanan di N, maka:
      • Jika lokasi tempat simple_name muncul dikelilingi oleh deklarasi namespace untuk N dan deklarasi namespace berisi extern_alias_directive atau using_alias_directive yang mengaitkan nama I dengan namespace atau tipe, maka simple_name menjadi ambigu dan terjadi kesalahan waktu kompilasi.
      • Jika tidak, simple_name mengacu pada namespace bernama I di N.
    • Jika tidak, jika N berisi jenis yang dapat diakses dengan nama I dan parameter jenis e, maka:
      • Jika e nol dan lokasi tempat simple_name terjadi diapit oleh deklarasi namespace untuk N dan deklarasi namespace berisi extern_alias_directive atau using_alias_directive yang mengaitkan nama I dengan namespace atau tipe, maka simple_name ambigu dan terjadi kesalahan pada waktu kompilasi.
      • Jika tidak, namespace_or_type_name mengacu pada tipe yang dibangun dengan argumen tipe yang diberikan.
    • Kecuali jika lokasi tempat terjadinya simple_name diapit oleh deklarasi namespace untuk N:
      • Jika e nol dan deklarasi namespace berisi extern_alias_directive atau using_alias_directive yang mengaitkan nama I dengan namespace atau jenis yang diimpor, maka simple_name mengacu pada namespace atau jenis tersebut.
      • Selain itu, jika namespace yang diimpor oleh using_namespace_directivedari deklarasi namespace berisi tepat satu tipe yang memiliki nama I dan parameter tipe e, maka simple_name mengacu pada tipe yang dibangun dengan argumen tipe yang diberikan.
      • Jika tidak, jika namespace yang diimpor oleh using_namespace_directivedari deklarasi namespace berisi lebih dari satu tipe dengan nama I dan parameter tipe e, maka simple_name menjadi ambigu dan terjadi kesalahan saat kompilasi.

    Catatan: Seluruh langkah ini sama persis dengan langkah yang sesuai dalam pemrosesan namespace_or_type_name (§7,8). catatan akhir

  • Jika tidak, jika e nol dan I merupakan pengidentifikasi _, simple_name adalah pembuangan sederhana, yang merupakan bentuk ekspresi deklarasi (§12,19).
  • Jika tidak, simple_name tidak terdefinisi dan terjadi kesalahan waktu kompilasi.

12.8.5 Ekspresi dalam tanda kurung

parenthesized_expression terdiri dari sebuah ekspresi yang diapit tanda kurung.

parenthesized_expression
    : '(' expression ')'
    ;

Ekspresi dengan tanda kurung dievaluasi dengan mengevaluasi ekspresi di dalam tanda kurung. Jika ekspresi dalam tanda kurung menunjukkan namespace atau jenis, kesalahan waktu kompilasi terjadi. Jika tidak, hasil dari ekspresi dalam tanda kurung adalah hasil evaluasi dari ekspresi yang terkandung.

12.8.6 Ekspresi tuple

tuple_expression mewakili tuple, dan terdiri dari dua atau lebih ekspresi yang dipisahkan koma dan dapat diberi nama opsional dalam tanda kurung. deconstruction_expression adalah sintaks singkat untuk tuple yang berisi ekspresi deklarasi yang diketik secara implisit.

tuple_expression
    : '(' tuple_element (',' tuple_element)+ ')'
    | deconstruction_expression
    ;
    
tuple_element
    : (identifier ':')? expression
    ;
    
deconstruction_expression
    : 'var' deconstruction_tuple
    ;
    
deconstruction_tuple
    : '(' deconstruction_element (',' deconstruction_element)+ ')'
    ;

deconstruction_element
    : deconstruction_tuple
    | identifier
    ;

tuple_expression diklasifikasikan sebagai sebuah tupel.

deconstruction_expressionvar (e1, ..., en) merupakan singkatan dari tuple_expression(var e1, ..., var en) dan mengikuti perilaku yang sama. Ini berlaku untuk setiap deconstruction_tupleyang bersarang di deconstruction_expressionsecara rekursif. Setiap pengidentifikasi yang ditumpuk dalam deconstruction_expression dengan demikian memperkenalkan ekspresi deklarasi (§12.19). Akibatnya, deconstruction_expression hanya dapat muncul di sisi kiri penugasan sederhana.

Contoh: Kode berikut mendeklarasikan tiga variabel: a, b, dan c. Masing-masing adalah bilangan bulat dan diberi nilainya dari tuple di sisi kanan penugasan.

var (a, b, c) = (1, 2, 3); // a is 1, b is 2, and c is 3.
var sum = a + b + c; // sum is 6.

Salah satu elemen individu dari penugasan itu sendiri dapat menjadi ekspresi dekonstruksi. Misalnya, ekspresi dekonstruksi berikut menetapkan enam variabel, a melalui f.

var (a, b, (c, d, (e, f))) = (1, 2, (3, 4, (5, 6)));

Dalam contoh ini, perhatikan bahwa struktur tuple berlapis harus cocok di kedua sisi penugasan.

Jika variabel di sisi kiri ditik secara implisit, ekspresi yang sesuai harus memiliki jenis:

(int a, string? b) = (42, null); //OK
var (c, d) = (42, null); // Invalid as type of d cannot be inferred
(int e, var f) = (42, null); // Invalid as type of f cannot be inferred

contoh akhir

Ekspresi tuple memiliki jenis jika dan hanya jika setiap ekspresi elemennya Ei memiliki jenis Ti. Jenis tersebut harus merupakan tipe tuple dengan aritas yang sama dengan ekspresi tuple, di mana setiap elemen ditentukan oleh yang berikut ini:

  • Jika elemen tuple dalam posisi yang sesuai memiliki nama Ni, maka elemen jenis tuple harus Ti Ni.
  • Jika tidak, jika Ei berbentuk Ni atau E.Ni atau E?.Ni maka elemen jenis tuple adalah Ti Ni, kecuali salah satu kondisi berikut:
    • Satu elemen lain dari ekspresi tuple memiliki nama Ni, atau
    • Elemen tuple lain tanpa nama memiliki ekspresi elemen tuple dalam bentuk Ni atau E.Ni atau E?.Ni.
    • Ni berformat ItemX, di mana X adalah urutan digit desimal yang dimulai non-0yang dapat mewakili posisi elemen tuple, dan X tidak mewakili posisi elemen.
  • Maka, elemen tipe tuple haruslah Ti.

Ekspresi tuple dievaluasi dengan mengevaluasi setiap ekspresi elemennya secara berurutan dari kiri ke kanan.

Nilai tuple dapat diperoleh dari ekspresi tuple dengan mengonversinya menjadi jenis tuple (§10.2.13), dengan mengklasifikasikannya kembali sebagai nilai (§12.2.2)) atau dengan menjadikannya target penugasan dekonstruksi (§12.23.2).

Contoh:

(int i, string) t1 = (i: 1, "One");
(long l, string) t2 = (l: 2, null);
var t3 = (i: 3, "Three");          // (int i, string)
var t4 = (i: 4, null);             // Error: no type

Dalam contoh ini, keempat ekspresi tuple adalah valid. Dua yang pertama, t1 dan t2, tidak menggunakan tipe dari ekspresi tuple, sebaliknya menggunakan konversi tuple implisit. Dalam kasus t2, konversi tuple implisit bergantung pada konversi implisit dari 2 ke long dan dari null ke string. Ekspresi tuple ketiga memiliki jenis (int i, string), dan oleh karena itu dapat diklasifikasikan ulang sebagai nilai dari jenis tersebut. Deklarasi t4, di lain pihak, adalah kesalahan: Ekspresi tuple tidak memiliki tipe karena elemen kedua tidak memiliki tipe.

if ((x, y).Equals((1, 2))) { ... };

Contoh ini menunjukkan bahwa tupel terkadang dapat menyebabkan beberapa lapisan tanda kurung yang banyak, terutama ketika ekspresi tupel adalah satu-satunya argumen untuk pemanggilan metode.

contoh akhir

12.8.7 Akses anggota

12.8.7.1 Umum

member_access terdiri dari primary_expression, predefined_type, atau qualified_alias_member, diikuti dengan token “.”, diikuti oleh identifier , secara opsional diikuti oleh type_argument_list.

member_access
    : primary_expression '.' identifier type_argument_list?
    | predefined_type '.' identifier type_argument_list?
    | qualified_alias_member '.' identifier type_argument_list?
    ;

predefined_type
    : 'bool' | 'byte' | 'char' | 'decimal' | 'double' | 'float' | 'int'
    | 'long' | 'object' | 'sbyte' | 'short' | 'string' | 'uint' | 'ulong'
    | 'ushort'
    ;

Produksi qualified_alias_member ditentukan dalam §14.8.

member_access adalah salah satu dari dua bentuk E.I atau E.I<A₁, ..., Aₑ>, di mana E adalah primary_expression, predefined_type atau qualified_alias_member,I adalah identifikator tunggal, dan <A₁, ..., Aₑ> adalah type_argument_listopsional. Ketika tidak ada type_argument_list yang ditentukan, pertimbangkan e menjadi nol.

Sebuah akses_anggota dengan ekspresi_utama jenis dynamic terikat secara dinamis (§12.3.3). Dalam hal ini, akses anggota diklasifikasikan sebagai akses properti jenis dynamic. Aturan berikut ini untuk menentukan makna member_access diterapkan saat run-time, menggunakan jenis run-time alih-alih jenis kompilasi dari primary_expression. Jika klasifikasi run-time ini mengarah ke grup metode, maka akses anggota akan menjadi primary_expression dari invocation_expression.

member_access dievaluasi dan diklasifikasikan sebagai berikut:

  • Jika e adalah nol dan E adalah namespace serta E berisi namespace bersarang dengan nama I, maka hasilnya adalah namespace tersebut.
  • Jika tidak, jika E adalah namespace dan E berisi tipe yang dapat diakses yang memiliki nama I dan parameter tipe K, maka hasilnya adalah tipe yang dibangun dengan argumen tipe yang diberikan.
  • Jika E diklasifikasikan sebagai jenis, jika E bukan parameter jenis, dan jika pencarian anggota (§12,5) I dalam E dengan parameter jenis K menghasilkan kecocokan, maka E.I dievaluasi dan diklasifikasikan sebagai berikut:

    Catatan: Ketika hasil pencarian anggota tersebut adalah grup metode dan K adalah nol, grup metode dapat berisi metode yang memiliki parameter jenis. Ini memungkinkan metode tersebut dapat dipertimbangkan untuk inferensi argumen jenis. catatan akhir

    • Jika I mengidentifikasi jenis, maka hasilnya adalah jenis yang dibangun dengan argumen jenis tertentu.
    • Jika I mengidentifikasi satu atau beberapa metode, maka hasilnya adalah grup metode tanpa ekspresi instans terkait.
    • Jika I mengidentifikasi properti statis, hasilnya adalah akses properti tanpa ekspresi instans terkait.
    • Jika I mengidentifikasi bidang statis:
      • Jika bidang dinyatakan baca-saja dan referensi terjadi di luar konstruktor statis kelas atau struktur tempat bidang dideklarasikan, maka hasilnya adalah nilai, yaitu nilai bidang statis I di E.
      • Jika tidak, hasilnya adalah variabel, yaitu bidang statis I di E.
    • Jika I mengidentifikasi peristiwa statis:
      • Jika referensi terjadi dalam kelas atau struktur tempat peristiwa dideklarasikan, dan peristiwa dinyatakan tanpa event_accessor_declarations (§15,8,1), maka E.I diproses persis seolah-olah I adalah bidang statis.
      • Jika tidak, hasilnya adalah akses peristiwa tanpa ekspresi instans terkait.
    • Jika I mengidentifikasi konstanta, maka hasilnya adalah nilai, yaitu nilai konstanta tersebut.
    • Jika I mengidentifikasi anggota enumerasi, maka hasilnya adalah nilai, yaitu nilai anggota enumerasi tersebut.
    • Jika tidak, E.I adalah referensi anggota yang tidak valid, dan terjadi kesalahan waktu kompilasi.
  • Jika E adalah akses properti, akses pengindeks, variabel, atau nilai, yang jenisnya T, dan pencarian anggota (§12,5) I dalam T dengan argumen jenis K menghasilkan kecocokan, maka E.I dievaluasi dan diklasifikasikan sebagai berikut:
    • Pertama, jika E adalah akses properti atau pengindeks, maka nilai akses properti atau pengindeks diperoleh (§12.2.2) dan E diklasifikasikan ulang sebagai nilai.
    • Jika I mengidentifikasi satu atau beberapa metode, maka hasilnya adalah grup metode dengan ekspresi instans terkait E.
    • Jika I mengidentifikasi properti instans, maka hasilnya adalah akses terhadap properti dengan ekspresi instans terkait E dan jenis yang terkait adalah jenis dari properti tersebut. Jika T adalah tipe kelas, tipe yang terkait dipilih dari deklarasi pertama atau penimpaan properti yang ditemukan dengan memulai dari T, dan mencari melalui kelas dasarnya.
    • Jika T adalah jenis kelas dan I mengidentifikasi sebuah bidang instans dari jenis kelas tersebut:
      • Jika nilai E adalah null, maka System.NullReferenceException akan dilemparkan.
      • Jika tidak, jika bidang hanya baca dan referensi terjadi di luar konstruktor instans kelas tempat bidang dideklarasikan, maka hasilnya adalah nilai, yaitu nilai bidang I dalam objek yang direferensikan oleh E.
      • Jika tidak, hasilnya adalah variabel, yaitu bidang I dalam objek yang dirujuk oleh E.
    • Jika T adalah struct_type dan I mengidentifikasi bidang instans dari struct_typetersebut :
      • Jika E adalah nilai, atau jika bidang adalah hanya-baca dan referensi terjadi di luar konstruktor instans dari struct yang di dalamnya bidang dideklarasikan, maka hasilnya adalah nilai, yakni nilai bidang I dalam instans struct yang diberikan oleh E.
      • Jika tidak, hasilnya adalah variabel, yaitu bidang I dalam instans struct yang diberikan oleh E.
    • Jika I mengidentifikasi kejadian khusus:
      • Jika referensi terjadi dalam kelas atau struktur tempat peristiwa dideklarasikan, dan peristiwa dinyatakan tanpa event_accessor_declarations (§15.8.1), dan referensi tidak terjadi sebagai sisi kiri operator a += atau -=, maka E.I diproses persis seolah-olah I adalah bidang instans.
      • Jika tidak, hasilnya adalah akses event dengan ekspresi instance terkait E.
  • Jika tidak, upaya dilakukan untuk memproses E.I sebagai pemanggilan metode ekstensi (§12.8.10.3). Jika ini gagal, E.I adalah referensi anggota yang tidak valid, dan terjadi kesalahan waktu pengikatan.

12.8.7.2 Nama sederhana dan nama jenis yang identik

Dalam akses anggota formulir E.I, jika E adalah pengidentifikasi tunggal, dan jika arti E sebagai simple_name (§12.8.4) adalah konstanta, bidang, properti, variabel lokal, atau parameter dengan jenis yang sama dengan arti E sebagai type_name (§§7.8.1), maka kedua kemungkinan arti E diizinkan. Pencarian anggota E.I tidak pernah ambigu, karena I harus menjadi anggota jenis E dalam kedua kasus. Dengan kata lain, aturan hanya mengizinkan akses ke anggota statis dan tipe bersarang dari E di mana seharusnya terjadi kesalahan waktu kompilasi.

Contoh:

struct Color
{
    public static readonly Color White = new Color(...);
    public static readonly Color Black = new Color(...);
    public Color Complement() => new Color(...);
}

class A
{
    public «Color» Color;              // Field Color of type Color

    void F()
    {
        Color = «Color».Black;         // Refers to Color.Black static member
        Color = Color.Complement();  // Invokes Complement() on Color field
    }

    static void G()
    {
        «Color» c = «Color».White;       // Refers to Color.White static member
    }
}

Hanya untuk tujuan eksposisi, dalam kelas A, kemunculan pengidentifikasi Color yang mengacu pada jenis Color dibatasi oleh «...», dan yang mengacu pada bidang Color tidak.

contoh akhir

12.8.8 Akses Anggota Bersyarat Null

null_conditional_member_access adalah versi bersyarat dari member_access (§12.8.7) dan merupakan kesalahan waktu pengikatan jika jenis hasilnya void. Untuk ekspresi kondisi null di mana jenis hasil mungkin void lihat (§12.8.11).

null_conditional_member_access terdiri dari primary_expression diikuti oleh dua token "?" dan ".", diikuti oleh pengidentifikasi dengan type_argument_list opsional, diikuti oleh nol atau lebih dependent_accesses mana pun yang dapat didahului oleh null_forgiving_operator.

null_conditional_member_access
    : primary_expression '?' '.' identifier type_argument_list?
      (null_forgiving_operator? dependent_access)*
    ;
    
dependent_access
    : '.' identifier type_argument_list?    // member access
    | '[' argument_list ']'                 // element access
    | '(' argument_list? ')'                // invocation
    ;

null_conditional_projection_initializer
    : primary_expression '?' '.' identifier type_argument_list?
    ;

Ekspresi null_conditional_member_accessE berbentuk P?.A. Arti E ditentukan sebagai berikut:

  • Jika tipe P adalah tipe nilai nullable:

    Biarkan T menjadi jenis P.Value.A.

    • Jika T adalah parameter jenis yang tidak diketahui sebagai jenis referensi atau jenis nilai yang tidak dapat diubah ke null, kesalahan waktu kompilasi terjadi.

    • Jika T adalah jenis nilai yang tidak dapat diubah ke null, maka jenis ET?, dan arti E sama dengan arti:

      ((object)P == null) ? (T?)null : P.Value.A
      

      Kecuali bahwa P dievaluasi hanya sekali.

    • Jika tidak, jenis dari E adalah T, dan arti E sama dengan arti:

      ((object)P == null) ? (T)null : P.Value.A
      

      Kecuali bahwa P dievaluasi hanya sekali.

  • Sebaliknya:

    Biarkan T menjadi jenis ekspresi P.A.

    • Jika T adalah parameter jenis yang tidak diketahui sebagai jenis referensi atau jenis nilai yang tidak dapat diubah ke null, kesalahan waktu kompilasi terjadi.

    • Jika T adalah jenis nilai yang tidak dapat diubah ke null, maka jenis ET?, dan arti E sama dengan arti:

      ((object)P == null) ? (T?)null : P.A
      

      Kecuali bahwa P dievaluasi hanya sekali.

    • Jika tidak, jenis dari E adalah T, dan arti E sama dengan arti:

      ((object)P == null) ? (T)null : P.A
      

      Kecuali bahwa P dievaluasi hanya sekali.

Catatan: Dalam ekspresi bentuk:

P?.A₀?.A₁

kemudian jika P menjadi null, maka baik A₀ maupun A₁ tidak dievaluasi. Hal yang sama berlaku jika ekspresi adalah rangkaian operasi null_conditional_member_access atau null_conditional_element_access§12.8.13.

catatan akhir

null_conditional_projection_initializer adalah pembatasan dari null_conditional_member_access dan memiliki makna yang sama. Ini hanya terjadi sebagai penginisialisasi proyeksi dalam ekspresi pembuatan objek anonim (§12.8.17.3).

12.8.9 Ekspresi pengampunan null

12.8.9.1 Umum

Nilai, jenis, klasifikasi ekspresi pengampunan-null (§12,2) dan konteks aman (§16.4.15) adalah nilai, jenis, klasifikasi, dan konteks aman primary_expression.

null_forgiving_expression
    : primary_expression null_forgiving_operator
    ;

null_forgiving_operator
    : '!'
    ;

Catatan: Operator pembatalan null postfik dan operator negasi logis prefiks (§12.9.4), meskipun diwakili oleh token leksikal yang sama (!), berbeda. Hanya yang terakhir yang mungkin kelebihan beban (§15.10), definisi operator pengampunan null diperbaiki. catatan akhir

Ini adalah kesalahan pada waktu kompilasi untuk menerapkan operator null-forgiving lebih dari sekali pada ekspresi yang sama, terlepas dari adanya tanda kurung di antaranya.

Contoh: berikut ini semua tidak valid:

var p = q!!;            // error: applying null_forgiving_operator more than once
var s = ( ( m(t) ! ) )! // error: null_forgiving_operator applied twice to m(t)

contoh akhir

Sisa subklausul ini dan subklaus terkait berikut bersifat normatif kondisional.

Pengkompilasi yang melakukan analisis status null statis (§8,9,5) harus sesuai dengan spesifikasi berikut.

Operator null-forgiving adalah pseudo-operasi waktu kompilasi yang digunakan untuk memberi tahu analisis status null statis oleh kompilator. Ini memiliki dua kegunaan: untuk mengambil alih penentuan kompilator bahwa ekspresi mungkin null; dan untuk mengambil alih kompilator yang mengeluarkan peringatan yang terkait dengan nullability.

Menerapkan operator null-forgiving ke ekspresi di mana analisis status null statis kompilator tidak menghasilkan peringatan apa pun bukan kesalahan.

12.8.9.2 Mengesampingkan penentuan "mungkin null"

Dalam beberapa keadaan, analisis status null statis kompilator dapat menentukan bahwa ekspresi memiliki status null mungkin null dan mengeluarkan peringatan diagnostik ketika informasi lain menunjukkan bahwa ekspresi tidak boleh null. Menerapkan operator "null-forgiving" pada ekspresi seperti itu memberi tahu analisis status null statis kompilator bahwa status null berada di dan bukan di; yang dapat mencegah peringatan diagnostik dan menginformasikan analisis yang sedang berlangsung.

Contoh: Pertimbangkan hal berikut:

#nullable enable
public static void M()
{
    Person? p = Find("John");                  // returns Person?
    if (IsValid(p))
    {
       Console.WriteLine($"Found {p!.Name}");  // p can't be null
    }
}

public static bool IsValid(Person? person) =>
    person != null && person.Name != null;

Jika IsValid mengembalikan true, p dapat dengan aman direferensikan untuk mengakses properti Name, dan peringatan "dereferensi terhadap kemungkinan nilai null" dapat dihilangkan menggunakan !.

contoh akhir

Contoh : Operator pengampunan null harus digunakan dengan hati-hati, pertimbangkan:

#nullable enable
int B(int? x)
{
    int y = (int)x!; // quash warning, throw at runtime if x is null
    return y;
}

Di sini operator pemaaf null diterapkan ke jenis nilai dan menghilangkan peringatan apa pun pada x. Namun jika x adalah null pada runtime, pengecualian akan terjadi karena null tidak dapat diubah ke int.

contoh akhir

12.8.9.3 Mengatasi peringatan analisis null lainnya

Selain mengganti penentuan mungkin null seperti yang dijelaskan sebelumnya, ada keadaan lain di mana mungkin diinginkan untuk mengganti penentuan analisis status null statis kompilator bahwa suatu ekspresi memerlukan satu atau beberapa peringatan. Menerapkan operator pengampunan null pada ekspresi tersebut meminta agar pengkompilasi tidak mengeluarkan peringatan apa pun untuk ekspresi itu. Menanggapi kompilator dapat memilih untuk tidak mengeluarkan peringatan dan juga dapat memodifikasi analisis lebih lanjut.

Contoh: Pertimbangkan hal berikut:

#nullable enable
public static void Assign(out string? lv, string? rv) { lv = rv; }

public string M(string? t)
{
    string s;
    Assign(out s!, t ?? "«argument was null»");
    return s;
}

Jenis parameter dari metode Assign, yaitu lv, &, rv, adalah string?, dengan lv menjadi parameter output, dan melakukan pengisian nilai sederhana.

Metode M meneruskan variabel s, jenis string, sebagai parameter output Assign, pengkompilasi yang digunakan mengeluarkan peringatan karena s bukan variabel nullable. Mengingat bahwa argumen kedua Assigntidak boleh null, operator pengabaian null digunakan untuk menghilangkan peringatan.

contoh akhir

Akhir teks normatif bersyarat.

12.8.10 Ekspresi pemanggilan

12.8.10.1 Umum

invocation_expression digunakan untuk memanggil suatu metode.

invocation_expression
    : primary_expression '(' argument_list? ')'
    ;

primary_expression dapat berupa null_forgiving_expression jika dan hanya jika ini memiliki delegate_type.

invocation_expression terikat secara dinamis (§12.3.3) jika setidaknya salah satu kondisi berikut ini berlaku:

  • primary_expression memiliki jenis waktu kompilasi dynamic.
  • Setidaknya satu argumen argument_list opsional memiliki jenis waktu kompilasi dynamic.

Dalam hal ini, invocation_expression diklasifikasikan sebagai nilai jenis dynamic. Aturan berikut ini untuk menentukan arti dari invocation_expression diterapkan kemudian saat run-time, dengan menggunakan jenis run-time daripada jenis waktu kompilasi dari primary_expression dan argumen yang memiliki jenis waktu kompilasi dynamic. Jika primary_expression tidak memiliki tipe waktu kompilasi dynamic, maka pemanggilan metodenya menjalani pemeriksaan waktu kompilasi terbatas sebagaimana dijelaskan dalam §12.6.5.

primary_expression dari invocation_expression harus berupa sebuah kelompok metode atau nilai dari delegate_type. Jika primary_expression adalah grup metode, invocation_expression adalah pemanggilan metode (§12.8.10.2). Jika primary_expression adalah nilai dari delegate_type, maka invocation_expression adalah suatu invokasi delegasi (§12.8.10.4). Jika primary_expression bukan grup metode atau nilai delegate_type, kesalahan waktu pengikatan terjadi.

argument_list opsional (§12.6.2) menyediakan nilai atau referensi variabel untuk parameter metode .

Hasil evaluasi invocation_expression diklasifikasikan sebagai berikut:

  • Jika invocation_expression memanggil metode yang tidak menghasilkan nilai (§15.6.1) atau delegasi yang tidak menghasilkan nilai, hasilnya tidak memberikan apa pun. Ekspresi yang diklasifikasikan sebagai tidak ada yang diizinkan hanya dalam konteks statement_expression (§13,7) atau sebagai isi lambda_expression (§12,21). Jika tidak, kesalahan pada saat pengikatan terjadi.
  • Jika tidak, apabila invocation_expression memanggil metode yang mengembalikan-oleh-referensi (§15.6.1) atau delegasi yang mengembalikan-oleh-referensi, hasilnya adalah variabel dengan tipe terkait dari tipe pengembalian metode atau delegasi tersebut. Jika pemanggilan adalah pada metode instance, dan penerima adalah dari jenis kelas T, tipe terkait dipilih dari deklarasi atau penimpaan metode pertama yang ditemukan saat memulai dengan T dan mencari melalui kelas dasarnya.
  • Jika tidak, invocation_expression memanggil metode yang mengembalikan-sebagai-nilai (§15.6.1) atau delegasi yang mengembalikan-sebagai-nilai, dan hasilnya adalah nilai dengan jenis terkait dari tipe pengembalian metode atau delegasi tersebut. Jika pemanggilan adalah pada metode instance, dan penerima adalah dari jenis kelas T, tipe terkait dipilih dari deklarasi atau penimpaan metode pertama yang ditemukan saat memulai dengan T dan mencari melalui kelas dasarnya.

12.8.10.2 Pemanggilan Metode

Untuk pemanggilan metode, primary_expression dari invocation_expression haruslah merupakan kelompok metode. Grup metode mengidentifikasi satu metode untuk dipanggil atau kumpulan metode yang kelebihan beban untuk memilih metode tertentu yang akan dipanggil. Dalam kasus terakhir, penentuan metode tertentu untuk dipanggil didasarkan pada konteks yang disediakan oleh jenis argumen dalam argument_list.

Pemrosesan waktu pengikatan pemanggilan metode dengan bentuk M(A), di mana M adalah grup metode (mungkin termasuk type_argument_list), dan A adalah opsional argument_list, terdiri dari langkah-langkah berikut:

  • Kumpulan metode kandidat untuk pemanggilan metode dibentuk. Untuk setiap metode F terkait dengan grup metode M:
    • Jika F bukan generik, F adalah kandidat saat:
      • M tidak memiliki daftar argumen tipe, dan
      • F berlaku sehubungan dengan A (§12.6.4.2).
    • Jika F umum dan M tidak memiliki daftar argumen jenis, F adalah kandidat saat:
      • Inferensi jenis (§12.6.3) berhasil, menyimpulkan daftar parameter tipe untuk panggilan, dan
      • Setelah argumen jenis yang disimpulkan digantikan untuk parameter jenis metode yang sesuai, semua jenis yang dibangun dalam daftar parameter F memenuhi batasannya (§8,4,5), dan daftar parameter F berlaku sehubungan dengan A (§12.6.4.2)
    • Jika F umum dan M menyertakan daftar argumen jenis, F adalah kandidat saat:
      • F memiliki jumlah parameter jenis metode yang sama seperti yang disediakan dalam daftar argumen jenis, dan
      • Setelah argumen jenis diganti untuk parameter jenis metode yang sesuai, semua jenis yang dibuat dalam daftar parameter F memenuhi batasannya (§8,4,5), dan daftar parameter F berlaku sehubungan dengan A (§12.6.4.2).
  • Himpunan metode kandidat dikurangi hanya berisi metode dari jenis turunan paling akhir: Untuk setiap metode C.F dalam himpunan, di mana C adalah jenis di mana metode F dideklarasikan, semua metode yang dideklarasikan dalam jenis dasar C dihapus dari himpunan. Selain itu, jika C adalah jenis kelas selain object, semua metode yang dideklarasikan dalam jenis antarmuka dihapus dari set.

    Catatan: Aturan terakhir ini hanya berpengaruh ketika kelompok metode merupakan hasil pencarian anggota pada parameter tipe yang memiliki kelas dasar yang efektif selain object dan set antarmuka efektif yang tidak kosong. catatan akhir

  • Jika set metode kandidat yang dihasilkan kosong, maka pemrosesan lebih lanjut berdasarkan langkah-langkah berikut dihentikan, dan sebaliknya upaya dilakukan untuk memproses pemanggilan sebagai pemanggilan metode ekstensi (§12.8.10.3). Jika ini gagal, maka tidak ada metode yang berlaku, dan kesalahan waktu pengikatan terjadi.
  • Metode terbaik dari kumpulan metode kandidat diidentifikasi menggunakan aturan penyelesaian overload menurut §12.6.4. Jika satu metode terbaik tidak dapat diidentifikasi, pemanggilan metode bersifat ambigu, dan terjadi kesalahan waktu pengikatan. Saat melakukan resolusi kelebihan beban, parameter metode generik dipertimbangkan setelah mengganti argumen jenis (disediakan atau disimpulkan) untuk parameter jenis metode yang sesuai.

Setelah metode dipilih dan divalidasi pada waktu pengikatan dengan langkah-langkah di atas, pemanggilan run-time aktual diproses sesuai dengan aturan pemanggilan anggota fungsi yang dijelaskan dalam §12.6.6.

Catatan: Efek intuitif dari aturan resolusi yang dijelaskan di atas adalah sebagai berikut: Untuk menemukan metode tertentu yang dipanggil oleh pemanggilan metode, mulailah dengan jenis yang ditunjukkan oleh pemanggilan metode dan lanjutkan rantai warisan sampai setidaknya satu deklarasi metode yang berlaku, dapat diakses, tidak diambil alih ditemukan. Kemudian lakukan inferensi tipe dan resolusi overload pada kumpulan metode yang berlaku, dapat diakses, dan tidak ditimpa yang dideklarasikan dalam tipe tersebut dan panggil metode yang dipilih. Jika tidak ada metode yang ditemukan, cobalah untuk memproses pemanggilan sebagai metode ekstensi. catatan akhir

12.8.10.3 Panggilan metode Ekstensi

Dalam pemanggilan metode (§12.6.6.2) dalam salah satu bentuk

«expr» . «identifier» ( )  
«expr» . «identifier» ( «args» )  
«expr» . «identifier» < «typeargs» > ( )  
«expr» . «identifier» < «typeargs» > ( «args» )

jika pemanggilan normal tidak menemukan metode yang dapat diterapkan, maka akan dilakukan upaya untuk memproses konstruk sebagai pemanggilan metode ekstensi. Jika «expr» atau salah satu «args» memiliki jenis waktu kompilasi dynamic, metode ekstensi tidak akan berlaku.

Tujuannya adalah untuk menemukan type_nameCterbaik , sehingga pemanggilan metode statis yang sesuai dapat berlangsung:

C . «identifier» ( «expr» )  
C . «identifier» ( «expr» , «args» )  
C . «identifier» < «typeargs» > ( «expr» )  
C . «identifier» < «typeargs» > ( «expr» , «args» )

Metode ekstensi Cᵢ.Mₑmemenuhi syarat jika:

  • Cᵢ adalah kelas non-generik dan tidak bersarang
  • Nama Mₑ adalah pengidentifikasi
  • Mₑ dapat diakses dan berlaku saat diterapkan ke argumen sebagai metode statis seperti yang ditunjukkan di atas
  • Identitas implisit, referensi, atau boxing conversion ada dari expr ke tipe parameter pertama dari Mₑ.

Pencarian C berlanjut sebagai berikut:

  • Dimulai dengan deklarasi namespace yang menutupi terdekat, berlanjut dengan setiap deklarasi namespace yang menutupi, dan berakhir dengan unit kompilasi yang mengandung, upaya terus-menerus dilakukan untuk menemukan sekumpulan kandidat metode ekstensi:
    • Jika namespace atau unit kompilasi yang diberikan secara langsung berisi deklarasi jenis non-generik Cᵢ dengan metode ekstensi yang memenuhi syarat Mₑmaka set metode ekstensi tersebut adalah set kandidat.
    • Jika namespace yang diimpor dengan menggunakan direktif namespace dalam namespace atau unit kompilasi yang diberikan secara langsung berisi deklarasi tipe non-generik Cᵢ dengan metode ekstensi yang memenuhi syarat Mₑ, maka kumpulan metode ekstensi tersebut adalah kumpulan kandidat.
  • Jika tidak ada set kandidat yang ditemukan dalam deklarasi namespace yang menyertakan atau unit kompilasi, kesalahan pada waktu kompilasi terjadi.
  • Jika tidak, resolusi kelebihan beban diterapkan ke kumpulan kandidat seperti yang dijelaskan dalam §12.6.4. Jika tidak ada satu metode terbaik yang ditemukan, kesalahan waktu kompilasi terjadi.
  • C adalah jenis di mana metode terbaik dinyatakan sebagai metode ekstensi.

Menggunakan C sebagai target, panggilan metode kemudian diproses sebagai pemanggilan metode statis (§12.6.6).

Catatan: Tidak seperti pemanggilan metode instans, tidak akan ada pengecualian ketika expr dievaluasi menjadi referensi null. Sebagai gantinya, nilai null ini diteruskan ke metode ekstensi seolah-olah melalui pemanggilan metode statis biasa. Terserah implementasi metode ekstensi untuk memutuskan cara merespons panggilan seperti itu. catatan akhir

Aturan sebelumnya berarti bahwa metode instans lebih diprioritaskan daripada metode ekstensi, bahwa metode ekstensi yang tersedia dalam deklarasi namespace bagian dalam lebih diutamakan daripada metode ekstensi yang tersedia dalam deklarasi namespace bagian luar, dan bahwa metode ekstensi yang dideklarasikan langsung di sebuah namespace lebih diutamakan daripada metode ekstensi yang diimpor ke namespace yang sama dengan menggunakan direktif 'using namespace'.

Contoh:

public static class E
{
    public static void F(this object obj, int i) { }
    public static void F(this object obj, string s) { }
}

class A { }

class B
{
    public void F(int i) { }
}

class C
{
    public void F(object obj) { }
}

class X
{
    static void Test(A a, B b, C c)
    {
        a.F(1);            // E.F(object, int)
        a.F("hello");      // E.F(object, string)
        b.F(1);            // B.F(int)
        b.F("hello");      // E.F(object, string)
        c.F(1);            // C.F(object)
        c.F("hello");      // C.F(object)
    }
}

Dalam contoh, metode Blebih diutamakan daripada metode ekstensi pertama, dan metode Clebih diutamakan daripada kedua metode ekstensi.

public static class C
{
    public static void F(this int i) => Console.WriteLine($"C.F({i})");
    public static void G(this int i) => Console.WriteLine($"C.G({i})");
    public static void H(this int i) => Console.WriteLine($"C.H({i})");
}

namespace N1
{
    public static class D
    {
        public static void F(this int i) => Console.WriteLine($"D.F({i})");
        public static void G(this int i) => Console.WriteLine($"D.G({i})");
    }
}

namespace N2
{
    using N1;

    public static class E
    {
        public static void F(this int i) => Console.WriteLine($"E.F({i})");
    }

    class Test
    {
        static void Main(string[] args)
        {
            1.F();
            2.G();
            3.H();
        }
    }
}

Output dari contoh ini adalah:

E.F(1)
D.G(2)
C.H(3)

D.G lebih diutamakan daripada C.G, dan E.F lebih diutamakan daripada dan D.FC.F.

contoh akhir

12.8.10.4 Mendelegasikan pemanggilan

Untuk pemanggilan delegasi, primary_expression dari invocation_expression haruslah merupakan nilai dari delegate_type. Selain itu, mempertimbangkan delegate_type menjadi anggota fungsi dengan daftar parameter yang sama dengan delegate_type, delegate_type akan berlaku (§12.6.4.2) sehubungan dengan argument_listinvocation_expression.

Pemrosesan run-time dari pemanggilan delegasi formulir D(A), di mana D adalah primary_expressiondelegate_type dan A adalah argument_listopsional , terdiri dari langkah-langkah berikut:

  • D dievaluasi. Jika evaluasi ini menyebabkan pengecualian, tidak ada langkah lebih lanjut yang dijalankan.
  • Daftar argumen A dievaluasi. Jika evaluasi ini menyebabkan pengecualian, tidak ada langkah lebih lanjut yang dijalankan.
  • Nilai D diperiksa agar valid. Jika nilai D adalah null, maka System.NullReferenceException dilempar dan tidak ada langkah lebih lanjut yang dijalankan.
  • Jika tidak, D adalah referensi ke sebuah instans delegasi. Pemanggilan anggota fungsi (§12.6.6) dilakukan pada setiap entitas yang dapat dipanggil dalam daftar pemanggilan delegasi. Untuk entitas yang dapat dipanggil dan terdiri dari sebuah instans dan metode instans, instans untuk pemanggilan adalah instans yang terdapat dalam entitas yang dapat dipanggil tersebut.

Lihat §21.6 untuk detail beberapa daftar pemanggilan tanpa parameter.

12.8.11 Ekspresi Pemanggilan Bersyarat Null

null_conditional_invocation_expression secara sintaksis entah null_conditional_member_access (§12.8.8) atau null_conditional_element_access (§12.8.13) di mana dependent_access akhir adalah ekspresi pemanggilan (§12.8.10).

Null_conditional_invocation_expression terjadi dalam konteks statement_expression (§13,7), anonymous_function_body (§12.21.1), atau method_body (§15.6.1).

Tidak seperti null_conditional_member_access atau null_conditional_element_accessyang setara secara sintetis, null_conditional_invocation_expression dapat diklasifikasikan sebagai apa-apa.

null_conditional_invocation_expression
    : null_conditional_member_access null_forgiving_operator? '(' argument_list? ')'
    | null_conditional_element_access null_forgiving_operator? '(' argument_list? ')'
    ;

null_forgiving_operator opsional hanya dapat disertakan jika dan hanya jika null_conditional_member_access atau null_conditional_element_access memiliki delegate_type.

Ekspresi null_conditional_invocation_expressionE adalah bentuk P?A; di mana A adalah sisa dari null_conditional_member_access atau null_conditional_element_accessyang setara secara sintaksis, A akan dimulai dari . atau [. Biarkan PA menandakan perangkaian P dan A.

Ketika E terjadi sebagai statement_expression, arti dari E sama dengan arti dari pernyataan :

if ((object)P != null) PA

kecuali bahwa P dievaluasi hanya sekali.

Ketika E terjadi sebagai badan_fungsi_anonim atau badan_metode, arti E tergantung pada klasifikasinya:

  • Jika E diklasifikasikan sebagai apa-apa maka maknanya sama dengan arti blok :

    { if ((object)P != null) PA; }
    

    kecuali bahwa P dievaluasi hanya sekali.

  • Jika tidak, arti E sama dengan arti blok :

    { return E; }
    

    dan pada gilirannya arti blok ini tergantung pada apakah E secara sintaksis setara dengan null_conditional_member_access (§12.8.8) atau null_conditional_element_access (§12.8.13).

12.8.12 Akses elemen

12.8.12.1 Umum

element_access terdiri dari primary_expression, diikuti dengan token ""[, diikuti oleh argument_list, diikuti dengan token ""]. argument_list terdiri dari satu atau beberapa argumen , dipisahkan oleh koma.

element_access
    : primary_expression '[' argument_list ']'
    ;

Saat mengenali primary_expression jika alternatif element_access dan pointer_element_access (§24.6.4) berlaku, maka yang terakhir akan dipilih jika primary_expression yang disematkan berjenis pointer (§24,3).

primary_expression dari element_access tidak boleh menjadi array_creation_expression kecuali jika mencakup array_initializer, atau menjadi stackalloc_expression kecuali jika mencakup stackalloc_initializer.

Catatan: Pembatasan ini ada untuk melarang kode yang berpotensi membingungkan seperti:

    var a = new int[3][1];

yang jika tidak ada akan ditafsirkan sebagai:

    var a = (new int[3])[1];

Pembatasan serupa berlaku untuk null_conditional_element_access (§12.8.13). catatan akhir

akses_elemen diikat secara dinamis (§12.3.3) jika setidaknya salah satu kondisi berikut terpenuhi:

  • primary_expression memiliki jenis waktu kompilasi dynamic.
  • Setidaknya satu ekspresi argument_list memiliki jenis waktu kompilasi dynamic.

Dalam hal ini jenis waktu kompilasi element_access tergantung pada jenis waktu kompilasi primary_expression: jika memiliki jenis array, maka jenis waktu kompilasi adalah jenis elemen dari jenis array tersebut; jika tidak, jenis waktu kompilasi adalah dynamic dan element_access diklasifikasikan sebagai nilai jenis dynamic. Aturan di bawah ini untuk menentukan arti dari element_access kemudian diterapkan pada waktu run-time, menggunakan tipe run-time alih-alih tipe waktu kompilasi dari ekspresi primary_expression dan argument_list yang memiliki tipe waktu kompilasi dynamic. Jika primary_expression tidak memiliki jenis waktu kompilasi dynamic, maka akses elemen mengalami pemeriksaan waktu kompilasi terbatas seperti yang dijelaskan dalam §12.6.5.

Contoh:

var index = (dynamic)1; // index has compile-time type dynamic
int[] a = {0, 1, 2};
var a_elem = a[index];  // dynamically bound, a_elem has compile-time type int
string s = "012";
var s_elem = s[index];  // dynamcially bound, s_elem has compile-time type dynamic

contoh akhir

Jika primary_expressionelement_access adalah:

  • nilai jenis array, element_access adalah akses array (§12.8.12.2);
  • nilai jenis string , element_access adalah akses string (§12.8.12.3);
  • jika tidak, primary_expression harus berupa variabel atau nilai kelas, struktur, atau jenis antarmuka yang memiliki satu atau beberapa anggota pengindeks, dalam hal ini element_access adalah akses pengindeks (§12.8.12.4).

12.8.12.2 Akses larik

Untuk akses array , argument_list tidak boleh berisi argumen bernama atau argumen referensi (§15.6.2.3).

Jumlah ekspresi dalam argument_list harus sama dengan peringkat array_type, dan setiap ekspresi adalah:

  • jenis int, uint, long, atau ulong; atau
  • untuk akses array peringkat tunggal saja, dari jenis Index atau Range; atau
  • dapat dikonversi secara implisit ke satu atau beberapa jenis di atas.

Pemrosesan run-time dari akses array formulir P[A], di mana P merupakan primary_expressiondari array_type dan A merupakan argument_list ekspresi indeks, terdiri dari langkah-langkah berikut:

  • P dievaluasi. Jika evaluasi ini menyebabkan pengecualian, tidak ada langkah lebih lanjut yang dijalankan.
  • Untuk setiap ekspresi indeks dalam argument_list secara berurutan, dari kiri ke kanan:
    • Ekspresi indeks dievaluasi, biarkan jenis nilai yang dihasilkan adalah T;
    • Nilai ini kemudian dikonversi ke jenis pertama: int, , uint, long, ulongatau untuk akses array peringkat tunggal saja, Index atau Range; yang konversi implisitnya (§10,2) dari T ada.
    • Jika evaluasi ekspresi indeks atau konversi implisit berikutnya menyebabkan pengecualian, maka tidak ada ekspresi indeks lebih lanjut yang dievaluasi dan tidak ada langkah lebih lanjut yang dijalankan.
  • Nilai P diperiksa agar valid. Jika nilai P adalah null, maka System.NullReferenceException dilempar dan tidak ada langkah lebih lanjut yang dijalankan.
  • Jika langkah-langkah sebelumnya telah menghasilkan satu nilai indeks jenis Range maka:
    • Biarkan L menjadi panjang array yang dirujuk oleh P.
    • A diperiksa agar valid sehubungan dengan L (§18.3). Jika tidak maka System.ArgumentOutOfRangeException dilemparkan dan tidak ada langkah-langkah lebih lanjut yang dijalankan.
    • Offset awal, S, dan jumlah item, N, sehubungan A dengan L ditentukan seperti yang dijelaskan untuk GetOffsetAndLength (§18.3).
    • Hasil dari akses array adalah array yang berisi salinan dangkal elemen N mulai dari P indeks S. Jika N nol, array memiliki elemen nol.

Nota:Baik S maupun N mungkin nol ($24,3). Mengindeks array kosong biasanya tidak valid, namun pengindeksan dengan rentang kosong mulai dari nol valid dan mengembalikan array kosong. Definisi ini juga memungkinkan S menjadi L, indeks akhir masa lalu (§18.1), dalam hal ini N akan menjadi nol dan array kosong yang dikembalikan. catatan akhir

Nota: Rentang elemen array tidak dapat ditetapkan untuk menggunakan akses array. Ini berbeda dari akses pengindeks (§12.8.12.4) yang mungkin, tetapi tidak perlu, mendukung penugasan ke rentang indeks yang ditentukan oleh Range nilai. catatan akhir

  • Sebaliknya:
    • Hasil evaluasi akses array adalah referensi variabel (§9,5) dari jenis elemen array.
    • Nilai setiap ekspresi dalam argument_list diperiksa terhadap batas aktual setiap dimensi instans array yang direferensikan oleh P. Jika satu atau beberapa nilai berada di luar rentang, System.IndexOutOfRangeException dilemparkan dan tidak ada langkah lebih lanjut yang dijalankan.
    • Referensi variabel elemen array yang diberikan oleh ekspresi indeks dihitung, dan ini menjadi hasil dari akses array.

12.8.12.3 Akses string

Untuk akses string , argument_listelement_access harus berisi satu argumen nilai yang tidak disebutkan namanya (§15.6.2.2) yang akan menjadi:

  • jenis int, Index atau Range; atau
  • secara implisit dapat dikonversi ke satu atau beberapa jenis di atas.

Pemrosesan run-time dari akses string formulir P[A], di mana P merupakan primary_expression jenis string dan A merupakan ekspresi tunggal, terdiri dari langkah-langkah berikut:

  • P dievaluasi. Jika evaluasi ini menyebabkan pengecualian, tidak ada langkah lebih lanjut yang dijalankan.
  • Ekspresi indeks dievaluasi, biarkan jenis nilai yang dihasilkan adalah T;
  • Nilai ini kemudian dikonversi ke yang pertama dari jenis: int, Index atau Range; yang konversi implisitnya (§10,2) dari T ada.
  • Jika evaluasi ekspresi indeks atau konversi implisit berikutnya menyebabkan pengecualian, maka tidak ada ekspresi indeks lebih lanjut yang dievaluasi dan tidak ada langkah lebih lanjut yang dijalankan.
  • Nilai P diperiksa agar valid. Jika nilai P adalah null, maka System.NullReferenceException dilempar dan tidak ada langkah lebih lanjut yang dijalankan.
  • Jika langkah-langkah sebelumnya telah menghasilkan nilai indeks jenis Range maka:
    • Hasil evaluasi akses string adalah nilai jenis string .
    • Biarkan L menjadi panjang string yang dirujuk oleh P.
    • A diperiksa agar valid sehubungan dengan L (§18.3), jika tidak maka System.ArgumentOutOfRangeException dilemparkan dan tidak ada langkah lebih lanjut yang dijalankan.
    • Offset awal, S, dan jumlah item, N, sehubungan A dengan L ditentukan seperti yang dijelaskan untuk GetOffsetAndLength (§18.3).
    • Hasil dari akses string adalah string yang dibentuk dengan menyalin karakter P mulai dari S, jika N adalah nol string kosong.

Nota:Baik S dan N mungkin nol (§18,3). Mengindeks string kosong biasanya tidak valid, namun pengindeksan dengan rentang kosong yang dimulai dari nol valid dan mengembalikan string kosong. Pendefinisian juga memungkinkan S menjadi L, indeks akhir masa lalu (§18.1), dalam hal ini N akan menjadi nol dan string kosong yang dikembalikan. catatan akhir

  • Sebaliknya:
    • Hasil evaluasi akses string adalah nilai jenis char .
    • Nilai ekspresi indeks yang dikonversi diperiksa terhadap batas aktual instans string yang direferensikan oleh P. Jika nilai di luar rentang, akan System.IndexOutOfRangeException dilemparkan dan tidak ada langkah lebih lanjut yang dijalankan.
    • Nilai karakter pada offset ekspresi indeks yang dikonversi dengan string P menjadi hasil akses string.

12.8.12.4 Akses pengindeks

Untuk akses pengindeks, primary_expressionelement_access harus berupa variabel atau nilai kelas, struktur, atau jenis antarmuka, dan jenis ini akan mengimplementasikan satu atau beberapa pengindeks yang berlaku sehubungan dengan argument_listelement_access. argument_list tidak boleh berisi out argumen atau ref .

Pemrosesan waktu pengikatan akses pengindeks dari bentuk P[A], di mana P adalah primary_expression dari tipe kelas, struktur, atau antarmuka T, dan A adalah argument_list, terdiri dari langkah-langkah berikut:

  • Set pengindeks yang disediakan oleh T telah dibentuk. Set ini terdiri dari semua pengindeks yang dideklarasikan dalam T atau jenis dasar T yang bukan merupakan deklarasi override dan dapat diakses dalam konteks saat ini (§7.5).
  • Set dibatasi pada pengindeks yang relevan dan tidak disembunyikan oleh pengindeks lain. Aturan berikut diterapkan ke setiap pengindeks S.I dalam set, di mana S adalah jenis di mana pengindeks I dinyatakan:
    • Jika I tidak berlaku sehubungan dengan A (§12.6.4.2), maka I dihapus dari set.
    • Jika I berlaku sehubungan dengan A (§12.6.4.2), maka semua pengindeks yang dinyatakan dalam jenis dasar S dihapus dari set.
    • Jika I berlaku sehubungan dengan A (§12.6.4.2) dan S adalah jenis kelas selain object, semua pengindeks yang dinyatakan dalam antarmuka dihapus dari set.
  • Jika kumpulan pengindeks kandidat yang dihasilkan kosong, maka tidak ada pengindeks yang berlaku, dan kesalahan waktu pengikatan terjadi.
  • Pengindeks terbaik dari set pengindeks kandidat diidentifikasi menggunakan aturan resolusi kelebihan beban §12.6.4. Jika satu pengindeks terbaik tidak dapat diidentifikasi, akses pengindeks ambigu, dan terjadi kesalahan waktu pengikatan.
  • Pengakses pengindeks terbaik diperiksa:
    • Jika akses pengindeks adalah target penugasan, pengindeks harus memiliki set atau ref get accessor, jika tidak, kesalahan waktu pengikatan terjadi;
    • Jika tidak, pengindeks akan memiliki get atau ref get accessor, jika tidak, kesalahan waktu pengikatan terjadi.

Pemrosesan runtime akses pengindeks terdiri dari langkah-langkah berikut:

  • Target primary_expressionP dievaluasi.
  • Ekspresi indeks argument_listA dievaluasi secara berurutan, dari kiri ke kanan.
  • Menggunakan pengindeks terbaik yang ditentukan pada waktu pengikatan:
    • Jika akses pengindeks adalah target penugasan, pengakses yang ditetapkan atau pengakses ref get dipanggil untuk menetapkan nilai baru (§12.23.2).
    • Dalam semua kasus lain, aksesor get atau ref get accessor dipanggil untuk mendapatkan nilai saat ini (§12.2.2).

12.8.13 Akses Elemen Bersyarat Null

null_conditional_element_access terdiri dari primary_expression diikuti oleh dua token "?" dan "[", diikuti oleh argument_list, diikuti oleh token "]", diikuti oleh nol atau lebih dependent_access yang salah satunya dapat didahului oleh null_forgiving_operator.

null_conditional_element_access
    : primary_expression '?' '[' argument_list ']'
      (null_forgiving_operator? dependent_access)*
    ;

Daftar argument_list dari null_conditional_element_access tidak boleh berisi atau mengandung argumen out atau ref.

Primary_expression dari null_conditional_element_access tidak boleh menjadi array_creation_expression kecuali jika menyertakan array_initializer, atau stackalloc_expression kecuali jika menyertakan stackalloc_initializer.

Catatan: Pembatasan ini ada untuk melarang kode yang berpotensi membingungkan. Pembatasan serupa berlaku untuk element_access (§12.8.12) di mana contoh apa yang dikecualikan dapat ditemukan. catatan akhir

null_conditional_element_access adalah versi kondisional dari element_access (§12.8.12) dan merupakan kesalahan saat pemrosesan jika jenis hasilnya void. Untuk ekspresi kondisi null di mana jenis hasil mungkin void lihat (§12.8.11).

Ekspresi null_conditional_element_accessE adalah dalam bentuk P?[A]B, di mana B adalah dependent_access, jika ada. Arti E ditentukan sebagai berikut:

  • Jika tipe P adalah tipe nilai nullable:

    Biarkan T menjadi jenis ekspresi P.Value[A]B.

    • Jika T adalah parameter jenis yang tidak diketahui sebagai jenis referensi atau jenis nilai yang tidak dapat diubah ke null, kesalahan waktu kompilasi terjadi.

    • Jika T adalah jenis nilai yang tidak dapat diubah ke null, maka jenis ET?, dan arti E sama dengan arti:

      ((object)P == null) ? (T?)null : P.Value[A]B
      

      Kecuali bahwa P dievaluasi hanya sekali.

    • Jika tidak, jenis dari E adalah T, dan arti E sama dengan arti:

      ((object)P == null) ? null : P.Value[A]B
      

      Kecuali bahwa P dievaluasi hanya sekali.

  • Sebaliknya:

    Biarkan T menjadi jenis ekspresi P[A]B.

    • Jika T adalah parameter jenis yang tidak diketahui sebagai jenis referensi atau jenis nilai yang tidak dapat diubah ke null, kesalahan waktu kompilasi terjadi.

    • Jika T adalah jenis nilai yang tidak dapat diubah ke null, maka jenis ET?, dan arti E sama dengan arti:

      ((object)P == null) ? (T?)null : P[A]B
      

      Kecuali bahwa P dievaluasi hanya sekali.

    • Jika tidak, jenis dari E adalah T, dan arti E sama dengan arti:

      ((object)P == null) ? null : P[A]B
      

      Kecuali bahwa P dievaluasi hanya sekali.

Catatan: Dalam ekspresi bentuk:

P?[A₀]?[A₁]

jika P mengevaluasi menjadi null maka baik A₀ maupun A₁ tidak dievaluasi. Hal yang sama berlaku jika ekspresi adalah urutan operasi null_conditional_element_access atau null_conditional_member_access§12.8.8.

catatan akhir

12.8.14 Akses ini

this_access terdiri dari kata kunci this.

this_access
    : 'this'
    ;

this_access hanya diizinkan dalam blok konstruktor instans, metode instans, aksesor instans (§12.2.1), atau finalizer. Ini memiliki salah satu arti berikut:

  • Saat this digunakan dalam primary_expression dalam konstruktor instans kelas, itu diklasifikasikan sebagai nilai. Jenis nilai adalah jenis instans (§15,3,2) dari kelas tempat penggunaan terjadi, dan nilainya adalah referensi ke objek yang sedang dibangun.
  • Saat this digunakan dalam primary_expression dalam metode instance atau aksesor instance dari kelas, itu diklasifikasikan sebagai nilai. Jenis nilai adalah jenis instans (§15,3,2) dari kelas tempat penggunaan terjadi, dan nilainya adalah referensi ke objek tempat metode atau aksesor dipanggil.
  • Ketika this digunakan dalam primary_expression dalam konstruktor instans struct, itu diklasifikasikan sebagai variabel. Jenis variabel adalah jenis instans (§15.3.2) dari struktur tempat penggunaan terjadi, dan variabel mewakili struktur yang sedang dibangun.
    • Jika deklarasi konstruktor tidak memiliki inisialisasi konstruktor, variabel this berperilaku sama persis dengan parameter output dari jenis struct. Secara khusus, ini berarti bahwa variabel harus ditetapkan secara definitif dalam setiap jalur eksekusi dari konstruktor instans.
    • Jika tidak, variabel this berperilaku sama persis dengan parameter ref dari jenis struct. Secara khusus, ini berarti bahwa variabel dianggap awalnya ditetapkan.
  • Saat this digunakan dalam primary_expression metode instans atau aksesor instans dari struct, itu diklasifikasikan sebagai variabel. Jenis variabel adalah jenis instans (§15.3.2) dari struct tempat penggunaannya terjadi.
    • Jika metode atau aksesor bukan iterator (§15,15) atau fungsi asinkron (§15.14), this variabel mewakili struktur tempat metode atau aksesor dipanggil.
      • Jika struct adalah readonly struct, variabel this berperilaku sama persis dengan parameter input dari jenis struct
      • Jika tidak, variabel this berperilaku sama persis dengan parameter ref dari jenis struct
    • Jika metode atau aksesor adalah fungsi iterator atau asinkron, variabel mewakili salinan dari struktur tempat metode atau aksesor dipanggil, dan berperilaku sama persis dengan nilai parameter jenis struct.

Penggunaan this dalam primary_expression dalam konteks selain yang tercantum di atas adalah kesalahan waktu kompilasi. Secara khusus, tidak mungkin untuk merujuk ke this dalam metode statis, pengakses properti statis, atau pada penginisialisasi variabel dari deklarasi bidang.

12.8.15 Akses dasar

base_access terdiri dari kata kunci base diikuti dengan token ".", sebuah pengidentifikasi, dan daftar_argumen_tipe yang opsional, atau daftar_argumen yang diapit tanda kurung siku:

base_access
    : 'base' '.' identifier type_argument_list?
    | 'base' '[' argument_list ']'
    ;

base_access digunakan untuk mengakses anggota kelas dasar yang disembunyikan oleh anggota bernama serupa di kelas atau struktur saat ini. base_access hanya diizinkan dalam isi konstruktor instans, metode instans, aksesor instans (§12.2.1), atau finalizer. Ketika base.I terjadi di kelas atau struct, I akan menunjukkan anggota kelas dasar kelas atau struct tersebut. Demikian juga, ketika base[E] terjadi di kelas, pengindeks yang berlaku akan ada di kelas dasar.

Pada waktu pengikatan, ekspresi base_access bentuk base.I dan base[E] dievaluasi persis seolah-olah mereka ditulis ((B)this).I dan ((B)this)[E], di mana B adalah kelas dasar kelas atau struktur tempat konstruksi tersebut terjadi. Dengan demikian, base.I dan base[E] sesuai dengan this.I dan this[E], kecuali this dilihat sebagai instans kelas dasar.

Ketika base_access mereferensikan anggota fungsi virtual (metode, properti, atau pengindeks), penentuan anggota fungsi mana yang akan dipanggil pada run-time (§12.6.6) diubah. Anggota fungsi yang dipanggil ditentukan dengan menemukan implementasi yang paling lengkap (§15.6.4) dari anggota fungsi dalam kaitannya dengan B (alih-alih berdasarkan tipe run-time this, seperti yang biasa dilakukan dalam akses non-dasar). Dengan demikian, dalam penggantian anggota fungsi virtual, base_access dapat digunakan untuk memanggil implementasi yang diwariskan dari anggota fungsi. Jika anggota fungsi yang dirujuk oleh base_access abstrak, kesalahan waktu pengikatan terjadi.

Catatan: Tidak seperti this, base bukan ekspresi dengan sendirinya. Ini adalah kata kunci yang hanya digunakan dalam konteks base_access atau constructor_initializer (§15.11.2). catatan akhir

12.8.16 Operator penambahan dan pengurangan Postfix

post_increment_expression
    : primary_expression '++'
    ;

post_decrement_expression
    : primary_expression '--'
    ;

Operan dari operasi kenaikan atau penurunan postfix harus merupakan ekspresi yang diklasifikasikan sebagai variabel, akses properti, atau akses pengindeks. Hasil operasi adalah nilai dengan jenis yang sama dengan operand.

Jika primary_expression memiliki jenis waktu kompilasi dynamic maka operator terikat secara dinamis (§12.3.3), post_increment_expression atau post_decrement_expression memiliki jenis waktu kompilasi dynamic dan aturan berikut diterapkan pada run-time menggunakan jenis run-time primary_expression.

Jika operan dari operasi penambahan atau pengurangan postfix adalah akses dari properti atau pengindeks, maka properti atau pengindeks tersebut harus memiliki aksesor get dan set. Jika ini tidak terjadi, kesalahan waktu pengikatan terjadi.

Resolusi kelebihan beban operator unary (§12.4.4) diterapkan untuk memilih implementasi operator tertentu. Operator ++ dan -- yang telah ditentukan sebelumnya ada untuk jenis berikut: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, dan jenis enum apa pun. Operator ++ yang telah ditentukan sebelumnya mengembalikan nilai yang dihasilkan dengan menambahkan 1 ke operand, dan operator -- yang telah ditentukan sebelumnya mengembalikan nilai yang dihasilkan dengan mengurangi 1 dari operand. Dalam konteks yang diperiksa, jika hasil penambahan atau pengurangan ini berada di luar rentang jenis hasil dan jenis hasilnya adalah jenis integral atau jenis enum, System.OverflowException dilemparkan.

Harus ada konversi implisit dari tipe pengembalian operator unary yang dipilih ke tipe primary_expression, jika tidak ada, terjadi kesalahan kompilasi.

Pemrosesan run-time dari operasi inkremen atau dekrement postfix dalam bentuk x++ atau x-- terdiri dari langkah-langkah berikut:

  • Jika x diklasifikasikan sebagai variabel:
    • x dievaluasi untuk menghasilkan variabel.
    • Nilai x disimpan.
    • Nilai x yang disimpan dikonversi ke jenis operand operator yang dipilih dan operator dipanggil dengan nilai ini sebagai argumennya.
    • Nilai yang dikembalikan oleh operator dikonversi ke jenis x dan disimpan di lokasi yang diberikan oleh evaluasi sebelumnya x.
    • Nilai x yang disimpan menjadi hasil operasi.
  • Jika x diklasifikasikan sebagai akses properti atau pengindeks:
    • Ekspresi instans (jika x tidak static) dan daftar argumen (jika x adalah akses pengindeks) yang terkait dengan x dievaluasi, dan hasilnya digunakan dalam pemanggilan get dan set aksesor berikutnya.
    • Aksesor get x dipanggil dan nilai yang dikembalikan disimpan.
    • Nilai x yang disimpan dikonversi ke jenis operand operator yang dipilih dan operator dipanggil dengan nilai ini sebagai argumennya.
    • Nilai yang dikembalikan oleh operator dikonversi ke jenis x dan aksesor set x dipanggil dengan nilai ini sebagai argumen nilainya.
    • Nilai x yang disimpan menjadi hasil operasi.

Operator ++ dan -- juga mendukung notasi awalan (§12.9.7). Hasil x++ atau x-- adalah nilai xsebelum operasi, sedangkan hasil ++x atau --x adalah nilai xsetelah operasi. Dalam kedua kasus, x itu sendiri memiliki nilai yang sama setelah operasi.

Penerapan operator ++ atau operator -- dapat dipanggil menggunakan notasi postfix atau prefiks. Tidak dimungkinkan untuk memiliki implementasi operator terpisah untuk dua notasi tersebut.

12.8.17 Operator baru

12.8.17.1 Umum

Operator new digunakan untuk membuat instans jenis baru.

Ada tiga bentuk ekspresi baru:

  • Ekspresi pembuatan objek digunakan untuk membuat instans baru jenis kelas dan jenis nilai.
  • Ekspresi pembuatan array digunakan untuk membuat instance baru dari tipe array.
  • Ekspresi pembuatan delegasi digunakan untuk mendapatkan instans tipe delegasi.

Operator new menyiratkan pembuatan instans dari suatu tipe, tetapi tidak selalu berarti alokasi memori. Secara khusus, instans jenis nilai tidak memerlukan memori tambahan di luar variabel tempatnya berada, dan tidak ada alokasi yang terjadi ketika new digunakan untuk membuat instans jenis nilai.

Catatan: Ekspresi pembentukan delegate tidak selalu membuat instance baru. Ketika ekspresi diproses dengan cara yang sama seperti konversi grup metode (§10,8) atau konversi fungsi anonim (§10,7) ini dapat mengakibatkan instans delegasi yang ada digunakan kembali. catatan akhir

12.8.17.2 Ekspresi pembuatan objek

12.8.17.2.1 Umum

object_creation_expression digunakan untuk menciptakan instans baru dari class_type atau value_type.

object_creation_expression
    : 'new' type '(' argument_list? ')' object_or_collection_initializer?
    | 'new' type object_or_collection_initializer
    ;

object_or_collection_initializer
    : object_initializer
    | collection_initializer
    ;

Jenis dari object_creation_expression adalah class_type, value_type, atau type_parameter. Jenis tidak dapat berupa tuple_type atau class_typeabstrak atau statis .

argument_list opsional (§12.6.2) diizinkan hanya jika tipe adalah class_type atau struct_type.

Ekspresi pembuatan objek dapat menghilangkan daftar argumen konstruktor dan mengapit tanda kurung asalkan menyertakan penginisialisasi objek atau penginisialisasi koleksi. Menghilangkan daftar argumen konstruktor dan mengapit tanda kurung setara dengan menentukan daftar argumen kosong.

Pemrosesan ekspresi pembuatan objek yang menyertakan penginisialisasi objek atau penginisialisasi koleksi terdiri dari pemrosesan konstruktor instans terlebih dahulu dan kemudian memproses inisialisasi anggota atau elemen yang ditentukan oleh penginisialisasi objek (§12.8.17.2.2) atau penginisialisasi koleksi (§12.8.17.2.3).

Jika salah satu argumen dalam argument_list opsional memiliki jenis waktu kompilasi dynamic maka object_creation_expression terikat secara dinamis (§12.3.3) dan aturan berikut diterapkan pada run-time menggunakan jenis run-time argumen argument_list yang memiliki jenis waktu kompilasi dynamic. Namun, pembuatan objek mengalami pemeriksaan waktu kompilasi terbatas seperti yang dijelaskan dalam §12.6.5.

Pemrosesan waktu pengikatan dari object_creation_expression dalam bentuk new T(A), di mana T adalah class_type, atau value_type, dan A adalah argument_list opsional, terdiri dari langkah-langkah berikut:

  • Jika T adalah value_type dan A tidak ada:
    • object_creation_expression adalah pemanggilan konstruktor bawaan. Hasil dari object_creation_expression adalah nilai jenis T, yaitu nilai default untuk T seperti yang didefinisikan dalam §8,3,3.
  • Jika tidak, jika T adalah type_parameter dan A tidak ada:
    • Jika tidak ada batasan jenis nilai atau batasan konstruktor (§15.2.5) telah ditentukan untuk T, kesalahan waktu pengikatan terjadi.
    • Hasil dari object_creation_expression adalah nilai dari tipe run-time yang terikat pada parameter tipe, yaitu hasil dari pemanggilan konstruktor default untuk tipe tersebut. Tipe runtime dapat berupa tipe referensi atau tipe nilai.
  • Jika tidak, jika T adalah class_type atau struct_type:
    • Jika T adalah class_typeabstrak atau statis, kesalahan waktu kompilasi terjadi.
    • Konstruktor instans yang akan dipanggil ditentukan menggunakan aturan resolusi kelebihan beban §12.6.4. Kumpulan konstruktor instans kandidat terdiri dari semua konstruktor instans yang dapat diakses yang dideklarasikan dalam T, yang berlaku sehubungan dengan A (§12.6.4.2). Jika kumpulan konstruktor instans kandidat kosong, atau jika satu konstruktor instans terbaik tidak dapat diidentifikasi, kesalahan waktu pengikatan terjadi.
    • Hasil dari object_creation_expression adalah nilai jenis T, yaitu nilai yang dihasilkan dengan memanggil konstruktor instans yang ditentukan pada langkah di atas.
    • Jika tidak, object_creation_expression tidak valid, dan terjadi kesalahan waktu pengikatan.

Bahkan jika object_creation_expression terikat secara dinamis, jenis waktu kompilasi masih T.

Pemrosesan run-time dari object_creation_expression dengan bentuk new T(A), di mana T adalah class_type atau struct_type dan A adalah argument_list opsional, terdiri dari langkah-langkah berikut:

  • Jika T adalah class_type:
    • Sebuah instance baru dari kelas T dialokasikan. Jika tidak ada cukup memori yang tersedia untuk mengalokasikan instans baru, System.OutOfMemoryException dilemparkan dan tidak ada langkah lebih lanjut yang dijalankan.
    • Semua bidang instans baru diinisialisasi ke nilai defaultnya (§9,3).
    • Konstruktor instans dipanggil sesuai dengan aturan pemanggilan anggota fungsi (§12.6.6). Referensi ke instans yang baru dialokasikan secara otomatis diteruskan ke konstruktor instans dan instans dapat diakses dari dalam konstruktor tersebut karena ini.
  • Jika T adalah struct_type:
    • Instans jenis T dibuat dengan mengalokasikan variabel lokal sementara. Karena konstruktor instans struct_type diperlukan untuk secara pasti menetapkan nilai ke setiap bidang instans yang dibuat, tidak ada inisialisasi variabel sementara yang diperlukan.
    • Konstruktor instans dipanggil sesuai dengan aturan pemanggilan anggota fungsi (§12.6.6). Referensi ke instans yang baru dialokasikan secara otomatis diteruskan ke konstruktor instans dan instans dapat diakses dari dalam konstruktor tersebut karena ini.
12.8.17.2.2 Penginisialisasi objek

Penginisialisasi objek menentukan nilai untuk nol atau beberapa bidang, properti, atau elemen terindeks objek.

object_initializer
    : '{' member_initializer_list? '}'
    | '{' member_initializer_list ',' '}'
    ;

member_initializer_list
    : member_initializer (',' member_initializer)*
    ;

member_initializer
    : initializer_target '=' initializer_value
    ;

initializer_target
    : identifier
    | '[' argument_list ']'
    ;

initializer_value
    : expression
    | object_or_collection_initializer
    ;

Penginisialisasi objek terdiri dari urutan penginisialisasi anggota, diapit oleh token { dan } dan dipisahkan oleh koma. Setiap member_initializer akan menetapkan target untuk inisialisasi. Pengidentifikasi harus memberi nama bidang atau properti objek yang dapat diakses yang sedang diinisialisasi, sedangkan argument_list yang diapit dalam tanda kurung siku harus menentukan argumen untuk pengindeks yang dapat diakses pada objek yang sedang diinisialisasi. Ini adalah kesalahan bagi penginisialisasi objek untuk menyertakan lebih dari satu penginisialisasi anggota untuk bidang atau properti yang sama.

Catatan: Meskipun penginisialisasi objek tidak diizinkan untuk mengatur bidang atau properti yang sama lebih dari sekali, tidak ada batasan seperti itu untuk pengindeks. Penginisialisasi objek dapat berisi beberapa target penginisialisasi yang mengacu pada pengindeks, dan bahkan dapat menggunakan argumen pengindeks yang sama beberapa kali. catatan akhir

Setiap initializer_target diikuti oleh tanda sama dengan dan harus disertai dengan sebuah ekspresi, penginisialisasi objek, atau penginisialisasi koleksi. Tidak dimungkinkan bagi ekspresi dalam penginisialisasi objek untuk merujuk ke objek yang baru dibuat yang diinisialisasi.

Dalam argument_listinitializer_target tidak ada dukungan implisit untuk argumen jenis Index (§18.4.2) atau Range (§18.4.3).

Penginisialisasi anggota yang menentukan ekspresi setelah tanda sama dengan diproses dengan cara yang sama seperti penugasan (§12.23.2) ke target.

Penginisialisasi anggota yang menentukan penginisialisasi objek setelah tanda sama dengan adalah penginisialisasi objek bertingkat, yaitu, inisialisasi dari objek yang tersemat. Alih-alih menetapkan nilai baru ke bidang atau properti, penugasan dalam penginisialisasi objek berlapis diperlakukan sebagai penugasan kepada anggota bidang atau properti. Penginisialisasi objek berlapis tidak dapat diterapkan ke properti dengan tipe nilai, atau ke bidang yang hanya bisa dibaca dengan tipe nilai.

Penginisialisasi anggota yang menentukan penginisialisasi koleksi setelah tanda sama dengan adalah inisialisasi koleksi tertanam. Alih-alih menetapkan koleksi baru ke bidang target, properti, atau pengindeks, elemen yang diberikan dalam penginisialisasi ditambahkan ke koleksi yang dirujuk oleh target. Target harus dari jenis koleksi yang memenuhi persyaratan yang ditentukan dalam §12.8.17.2.3.

Ketika target inisialisasi mengacu pada pengindeks, argumen ke pengindeks akan selalu dievaluasi tepat sekali. Dengan demikian, bahkan jika argumen akhirnya tidak pernah digunakan (misalnya, karena inisialisasi berlapis kosong), argumen tersebut dievaluasi untuk efek sampingnya.

Contoh: Kelas berikut mewakili titik dengan dua koordinat:

public class Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

Instans Point dapat dibuat dan diinisialisasi sebagai berikut:

Point a = new Point { X = 0, Y = 1 };

Ini memiliki efek yang sama dengan

Point __a = new Point();
__a.X = 0;
__a.Y = 1;
Point a = __a;

di mana __a adalah variabel sementara yang tidak terlihat dan tidak dapat diakses.

Kelas berikut menunjukkan persegi panjang yang dibuat dari dua titik, dan pembuatan dan inisialisasi instans Rectangle:

public class Rectangle
{
    public Point P1 { get; set; }
    public Point P2 { get; set; }
}

Instans Rectangle dapat dibuat dan diinisialisasi sebagai berikut:

Rectangle r = new Rectangle
{
    P1 = new Point { X = 0, Y = 1 },
    P2 = new Point { X = 2, Y = 3 }
};

Ini memiliki efek yang sama dengan

Rectangle __r = new Rectangle();
Point __p1 = new Point();
__p1.X = 0;
__p1.Y = 1;
__r.P1 = __p1;
Point __p2 = new Point();
__p2.X = 2;
__p2.Y = 3;
__r.P2 = __p2;
Rectangle r = __r;

di mana __r, __p1 dan __p2 adalah variabel sementara yang tidak terlihat dan tidak dapat diakses.

Jika konstruktor Rectanglemengalokasikan dua instans Point yang disematkan, konstruktor tersebut dapat digunakan untuk menginisialisasi instans Point yang disematkan alih-alih menetapkan instans baru:

public class Rectangle
{
    public Point P1 { get; } = new Point();
    public Point P2 { get; } = new Point();
}

konstruksi berikut dapat digunakan untuk menginisialisasi instans Point yang disematkan alih-alih menetapkan instans baru:

Rectangle r = new Rectangle
{
    P1 = { X = 0, Y = 1 },
    P2 = { X = 2, Y = 3 }
};

Ini memiliki efek yang sama dengan

Rectangle __r = new Rectangle();
__r.P1.X = 0;
__r.P1.Y = 1;
__r.P2.X = 2;
__r.P2.Y = 3;
Rectangle r = __r;

contoh akhir

12.8.17.2.3 Penginisialisasi koleksi

Penginisialisasi koleksi menentukan elemen koleksi.

collection_initializer
    : '{' element_initializer_list '}'
    | '{' element_initializer_list ',' '}'
    ;

element_initializer_list
    : element_initializer (',' element_initializer)*
    ;

element_initializer
    : non_assignment_expression
    | '{' expression_list '}'
    ;

expression_list
    : expression (',' expression)*
    ;

Penginisialisasi koleksi terdiri dari urutan penginisialisasi elemen, dibatasi oleh token { dan } dan dipisahkan dengan koma. Setiap penginisialisasi elemen menentukan elemen yang akan ditambahkan ke objek koleksi yang sedang diinisialisasi, dan terdiri dari daftar ekspresi yang diapit oleh token { dan } dan dipisahkan oleh koma. Penginisialisasi elemen yang berisi satu ekspresi dapat ditulis tanpa kurung kurawal, tetapi kemudian tidak dapat menjadi ekspresi penetapan, guna menghindari kebingungan dengan penginisialisasi anggota. Produksi non_assignment_expression didefinisikan dalam §12.24.

Contoh: Berikut ini adalah contoh ekspresi pembuatan objek yang menyertakan penginisialisasi koleksi:

List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

contoh akhir

Objek koleksi di mana penginisialisasi koleksi diterapkan harus dari jenis yang mengimplementasikan System.Collections.IEnumerable atau akan terjadi kesalahan waktu kompilasi. Untuk setiap elemen yang ditentukan secara berurutan dari kiri ke kanan, pencarian anggota normal diterapkan untuk menemukan anggota bernama Add. Jika hasil pencarian anggota bukan grup metode, kesalahan waktu kompilasi terjadi. Jika tidak, proses penyelesaian kelebihan beban diterapkan dengan daftar ekspresi dari penginisialisasi elemen sebagai daftar argumen, dan penginisialisasi koleksi tersebut memanggil metode yang dihasilkan. Dengan demikian, objek koleksi harus berisi instans atau metode ekstensi yang berlaku dengan nama Add untuk setiap penginisialisasi elemen.

Contoh: Berikut ini memperlihatkan kelas yang mewakili kontak dengan nama dan daftar nomor telepon, serta pembuatan dan inisialisasi List<Contact>:

public class Contact
{
    public string Name { get; set; }
    public List<string> PhoneNumbers { get; } = new List<string>();
}

class A
{
    static void M()
    {
        var contacts = new List<Contact>
        {
            new Contact
            {
                Name = "Chris Smith",
                PhoneNumbers = { "206-555-0101", "425-882-8080" }
            },
            new Contact
            {
                Name = "Bob Harris",
                PhoneNumbers = { "650-555-0199" }
            }
        };
    }
}

yang memiliki efek yang sama dengan

var __clist = new List<Contact>();
Contact __c1 = new Contact();
__c1.Name = "Chris Smith";
__c1.PhoneNumbers.Add("206-555-0101");
__c1.PhoneNumbers.Add("425-882-8080");
__clist.Add(__c1);
Contact __c2 = new Contact();
__c2.Name = "Bob Harris";
__c2.PhoneNumbers.Add("650-555-0199");
__clist.Add(__c2);
var contacts = __clist;

di mana __clist, __c1 dan __c2 adalah variabel sementara yang tidak terlihat dan tidak dapat diakses.

contoh akhir

12.8.17.3 Ekspresi pembuatan objek anonim

anonymous_object_creation_expression digunakan untuk membuat objek dari jenis anonim.

anonymous_object_creation_expression
    : 'new' anonymous_object_initializer
    ;

anonymous_object_initializer
    : '{' member_declarator_list? '}'
    | '{' member_declarator_list ',' '}'
    ;

member_declarator_list
    : member_declarator (',' member_declarator)*
    ;

member_declarator
    : simple_name
    | member_access
    | null_conditional_projection_initializer
    | base_access
    | identifier '=' expression
    ;

Inisialisasi objek anonim mendeklarasikan tipe anonim dan mengembalikan instance dari tipe tersebut. Jenis anonim adalah jenis kelas tanpa nama yang mewarisi langsung dari object. Anggota tipe anonim adalah urutan properti hanya-baca yang disimpulkan dari penginisialisasi objek anonim yang digunakan untuk membuat instance dari tipe tersebut. Secara khusus, penginisialisasi objek anonim formulir

new { p₁=e₁,p₂=e₂, ... pv=ev}

mendeklarasikan jenis formulir anonim

class __Anonymous1
{
    private readonly «T1» «f1»;
    private readonly «T2» «f2»;
    ...
    private readonly «Tn» «fn»;

    public __Anonymous1(«T1» «a1», «T2» «a2»,..., «Tn» «an»)
    {
        «f1» = «a1»;
        «f2» = «a2»;
        ...
        «fn» = «an»;
    }

    public «T1» «p1» { get { return «f1»; } }
    public «T2» «p2» { get { return «f2»; } }
    ...
    public «Tn» «pn» { get { return «fn»; } }
    public override bool Equals(object __o) { ... }
    public override int GetHashCode() { ... }
}

di mana setiap «Tx» adalah jenis dari ekspresi «ex» yang sesuai. Ekspresi yang digunakan dalam member_declarator harus memiliki tipe. Dengan demikian, terjadi kesalahan waktu kompilasi untuk ekspresi dalam member_declarator yang menjadi null atau fungsi anonim.

Nama jenis anonim dan parameter ke metode Equals secara otomatis dihasilkan oleh pengompilasi dan tidak dapat dirujuk dalam teks program.

Dalam program yang sama, dua penginisialisasi objek anonim yang menentukan urutan properti dengan nama yang sama dan jenis waktu kompilasi dalam urutan yang sama akan menghasilkan instans dengan jenis anonim yang sama.

Contoh: Dalam contoh

var p1 = new { Name = "Lawnmower", Price = 495.00 };
var p2 = new { Name = "Shovel", Price = 26.95 };
p1 = p2;

penugasan pada baris terakhir diizinkan karena p1 dan p2 memiliki jenis anonim yang sama.

contoh akhir

Metode Equals dan GetHashcode pada jenis anonim menggantikan metode yang diwariskan dari object, dan didefinisikan berdasarkan Equals dan GetHashcode dari properti, sehingga dua instance dari jenis anonim yang sama dianggap setara jika dan hanya jika seluruh propertinya identik.

Deklarator anggota dapat disingkat dengan nama sederhana (§12.8.4), akses anggota (§12.8.7), penginisialisasi proyeksi kondisional null §12.8.8 atau akses dasar (§12.8.15). Ini disebut inisialisasi proyeksi dan merupakan singkatan untuk deklarasi dan penugasan properti dengan nama yang sama. Secara khusus, anggota deklarator formulir

«identifier», «expr» . «identifier», dan «expr» ? . «identifier»

setara persis dengan yang berikut, masing-masing:

«identifier» = «identifier», «identifier» = «expr» . «identifier», dan «identifier» = «expr» ? . «identifier»

Dengan demikian, dalam penginisialisasi proyeksi, pengidentifikasi memilih nilai dan bidang atau properti tempat nilai ditetapkan. Secara intuitif, penginisialisasi proyeksi tidak hanya memproyeksikan nilai, tetapi juga nama nilai.

12.8.17.4 Ekspresi pembuatan array

array_creation_expression digunakan untuk membuat contoh baru array_type.

array_creation_expression
    : 'new' non_array_type '[' expression_list ']' rank_specifier*
      array_initializer?
    | 'new' array_type array_initializer
    | 'new' rank_specifier array_initializer
    ;

Ekspresi pembuatan array dari bentuk pertama mengalokasikan instans array dari jenis yang dihasilkan setelah penghapusan setiap ekspresi individu dari daftar ekspresi.

Contoh: Ekspresi pembuatan array new int[10,20] menghasilkan instans array jenis int[,], dan ekspresi pembuatan array int[10][,] baru menghasilkan instans array jenis int[][,]. contoh akhir

Setiap ekspresi dalam daftar ekspresi harus berjenis int, uint, long, atau ulong, atau secara implisit dapat dikonversi ke satu atau beberapa jenis ini. Nilai setiap ekspresi menentukan panjang dimensi yang sesuai dalam instans array yang baru dialokasikan. Karena panjang dimensi array harus nonnegatif, ini adalah kesalahan pada waktu kompilasi untuk menggunakan ekspresi konstanta dengan nilai negatif dalam daftar ekspresi.

Kecuali dalam konteks yang tidak aman (§24.2), tata letak array tidak ditentukan.

Jika ekspresi pembuatan array dari formulir pertama menyertakan penginisialisasi array, setiap ekspresi dalam daftar ekspresi harus berupa konstanta dan panjang peringkat dan dimensi yang ditentukan oleh daftar ekspresi harus cocok dengan penginisialisasi array.

Dalam ekspresi pembuatan array dari bentuk kedua atau ketiga, tingkat jenis array atau spesifikasi tingkat yang ditentukan harus sesuai dengan penginisialisasi array. Panjang dimensi individual disimpulkan dari jumlah elemen di setiap tingkat bertingkat yang sesuai dari penginisialisasi array. Oleh karena itu, ekspresi penginisialisasi dalam deklarasi berikut

var a = new int[,] {{0, 1}, {2, 3}, {4, 5}};

tepat sekali sesuai dengan

var a = new int[3, 2] {{0, 1}, {2, 3}, {4, 5}};

Ekspresi pembuatan array dari formulir ketiga disebut sebagai ekspresi pembuatan array yang ditik secara implisit. Ini mirip dengan bentuk kedua, kecuali bahwa jenis elemen array tidak diberikan secara eksplisit, tetapi ditentukan sebagai jenis umum terbaik (§12.6.3.16) dari kumpulan ekspresi dalam penginisialisasi array. Untuk array multidimensi, maksudnya salah satu di mana rank_specifier berisi setidaknya satu koma, set ini terdiri dari semua ekspresi yang ditemukan dalam array_initializerberlapis.

Penginisialisasi array dijelaskan lebih lanjut dalam §17.7.

Hasil evaluasi ekspresi pembuatan array diklasifikasikan sebagai nilai, yaitu referensi ke instans array yang baru dialokasikan. Pemrosesan run-time ekspresi pembuatan array terdiri dari langkah-langkah berikut:

  • Ekspresi panjang dimensi expression_list dievaluasi secara berurutan, dari kiri ke kanan. Setelah evaluasi setiap ekspresi, konversi implisit (§10,2) ke salah satu jenis berikut dilakukan: int, uint, long, ulong. Jenis pertama dalam daftar ini yang memiliki konversi implisit akan dipilih. Jika evaluasi ekspresi atau konversi implisit berikutnya menyebabkan pengecualian, maka tidak ada ekspresi lebih lanjut yang dievaluasi dan tidak ada langkah lebih lanjut yang dijalankan.
  • Nilai komputasi untuk panjang dimensi divalidasi, sebagai berikut: Jika satu atau beberapa nilai kurang dari nol, System.OverflowException dilemparkan dan tidak ada langkah lebih lanjut yang dijalankan.
  • Instans array dengan panjang dimensi yang diberikan dialokasikan. Jika tidak ada cukup memori yang tersedia untuk mengalokasikan instans baru, System.OutOfMemoryException dilemparkan dan tidak ada langkah lebih lanjut yang dijalankan.
  • Semua elemen instans array baru diinisialisasi ke nilai defaultnya (§9,3).
  • Jika ekspresi pembuatan array berisi penginisialisasi array, maka setiap ekspresi dalam penginisialisasi array dievaluasi dan ditetapkan ke elemen array yang sesuai. Evaluasi dan penetapan dilakukan dalam urutan ekspresi ditulis dalam penginisialisasi array—dengan kata lain, elemen diinisialisasi dalam urutan indeks yang meningkat, dengan dimensi paling kanan bertambah terlebih dahulu. Jika evaluasi ekspresi tertentu atau penetapan berikutnya ke elemen array yang sesuai menyebabkan pengecualian, maka tidak ada elemen lebih lanjut yang diinisialisasi (dan elemen yang tersisa dengan demikian akan memiliki nilai defaultnya).

Pernyataan pembuatan array memungkinkan pembuatan array dengan elemen bertipe array, tetapi elemen-elemen array tersebut harus diinisialisasi secara manual.

Contoh: Pernyataan

int[][] a = new int[100][];

membuat array berdimensi tunggal dengan 100 elemen bertipe int[]. Nilai awal setiap elemen null. Tidak mungkin bagi ekspresi pembuatan array yang sama untuk juga menginstansiasi sub-array, dan pernyataan

int[][] a = new int[100][5]; // Error

menghasilkan kesalahan waktu kompilasi. Proses instansiasi sub-array dapat dilakukan secara manual, seperti halnya dalam

int[][] a = new int[100][];
for (int i = 0; i < 100; i++)
{
    a[i] = new int[5];
}

contoh akhir

Catatan: Ketika array memiliki bentuk "persegi panjang", yaitu ketika sub-array memiliki panjang yang sama, lebih efisien untuk menggunakan array multi-dimensi. Dalam contoh di atas, pembuatan instance array dalam array menghasilkan 101 objek—satu array luar dan 100 sub-array. Sebaliknya,

int[,] a = new int[100, 5];

hanya membuat satu objek berupa array dua dimensi dan menyelesaikan alokasi dalam satu pernyataan.

catatan akhir

Contoh: Berikut ini adalah contoh ekspresi pembuatan array yang ditik secara implisit:

var a = new[] { 1, 10, 100, 1000 };                     // int[]
var b = new[] { 1, 1.5, 2, 2.5 };                       // double[]
var c = new[,] { { "hello", null }, { "world", "!" } }; // string[,]
var d = new[] { 1, "one", 2, "two" };                   // Error

Ekspresi terakhir menyebabkan kesalahan waktu kompilasi karena tidak int atau string secara implisit dapat dikonversi ke yang lain, sehingga tidak ada jenis umum terbaik. Ekspresi pembuatan array bertipe eksplisit harus digunakan dalam kasus ini, misalnya dengan menentukan tipe sebagai object[]. Atau, salah satu elemen dapat dilemparkan ke jenis dasar umum, yang kemudian akan menjadi jenis elemen yang disimpulkan.

contoh akhir

Ekspresi pembuatan array yang di ketik secara implisit dapat dikombinasikan dengan penginisialisasi objek anonim (§12.8.17.3) untuk membuat struktur data yang di ketik secara anonim.

Contoh:

var contacts = new[]
{
    new
    {
        Name = "Chris Smith",
        PhoneNumbers = new[] { "206-555-0101", "425-882-8080" }
    },
    new 
    {
        Name = "Bob Harris",
       PhoneNumbers = new[] { "650-555-0199" }
    }
};

contoh akhir

12.8.17.5 Delegasikan ekspresi pembuatan

delegate_creation_expression digunakan untuk mendapatkan instance delegate_type.

delegate_creation_expression
    : 'new' delegate_type '(' expression ')'
    ;

Argumen ekspresi pembuatan delegasi adalah grup metode, fungsi anonim, atau nilai jenis waktu kompilasi dynamic atau delegate_type. Jika argumen adalah grup metode, maka argumen itu mengidentifikasi metode dan, untuk metode instans, mengidentifikasi objek yang akan dibuatkan delegasinya. Jika argumen adalah fungsi anonim, argumen secara langsung menentukan parameter dan isi metode target delegasi. Jika argumen adalah nilai, argumen tersebut mengidentifikasi instans delegasi dari mana salinan akan dibuat.

Jika ekspresi memiliki jenis waktu kompilasi dynamic, delegate_creation_expression terikat secara dinamis (§12.8.17.5), dan aturan di bawah ini diterapkan pada run-time menggunakan jenis run-time ekspresi . Jika tidak, aturan diterapkan pada waktu kompilasi.

Pemrosesan waktu pengikatan delegate_creation_expression dari bentuk baru D(E), di mana D adalah delegate_type dan E adalah ekspresi , terdiri dari langkah-langkah berikut:

  • Jika E adalah grup metode, ekspresi pembuatan delegasi diproses dengan cara yang sama seperti konversi grup metode (§10,8) dari E ke D.

  • Jika E adalah fungsi anonim, ekspresi pembuatan delegasi diproses dengan cara yang sama seperti konversi fungsi anonim (§10,7) dari E ke D.

  • Jika E adalah nilai, E harus kompatibel (§21.2) dengan D, dan hasilnya adalah referensi ke delegasi yang baru dibuat dengan daftar pemanggilan entri tunggal yang memanggil E.

Pemrosesan run-time delegate_creation_expression dari bentuk new D(E), di mana D adalah sebuah delegate_type dan E adalah sebuah ekspresi , terdiri dari langkah-langkah berikut:

  • Jika E adalah grup metode, ekspresi pembuatan delegasi dievaluasi sebagai konversi grup metode (§10,8) dari E ke D.
  • Jika E adalah fungsi anonim, pembuatan delegasi dievaluasi sebagai konversi fungsi anonim dari E ke D (§10,7).
  • Jika adalah nilai dari tipe delegasi:
    • E dievaluasi. Jika evaluasi ini menyebabkan pengecualian, tidak ada langkah lebih lanjut yang dijalankan.
    • Jika nilai E adalah null, maka System.NullReferenceException dilempar dan tidak ada langkah lebih lanjut yang dijalankan.
    • Sebuah instance baru dari jenis delegate D dialokasikan. Jika tidak ada cukup memori yang tersedia untuk mengalokasikan instans baru, System.OutOfMemoryException dilemparkan dan tidak ada langkah lebih lanjut yang dijalankan.
    • Instans delegasi baru diinisialisasi dengan daftar pemanggilan dengan satu entri yang memanggil E.

Daftar pemanggilan delegasi ditentukan ketika delegasi dibuat dan kemudian tetap konstan selama seluruh masa pakai delegasi. Dengan kata lain, tidak mungkin untuk mengubah entitas yang dapat dipanggil dari sebuah delegasi setelah entitas tersebut dibuat.

Catatan: Ingat, ketika dua delegasi digabungkan atau satu dihapus dari yang lain, maka akan menghasilkan delegasi baru; tidak ada delegasi yang sudah ada yang mengalami perubahan isi. catatan akhir

Tidak dimungkinkan untuk membuat delegasi yang mengacu pada properti, pengindeks, operator yang ditentukan pengguna, konstruktor instans, finalizer, atau konstruktor statis.

Contoh: Seperti yang dijelaskan di atas, saat delegasi dibuat dari grup metode, daftar parameter dan jenis pengembalian delegasi menentukan metode mana yang kelebihan beban yang akan dipilih. Dalam contoh

delegate double DoubleFunc(double x);

class A
{
    DoubleFunc f = new DoubleFunc(Square);

    static float Square(float x) => x * x;
    static double Square(double x) => x * x;
}

bidang A.f diinisialisasi dengan delegasi yang mengacu pada metode Square kedua karena metode tersebut sama persis dengan daftar parameter dan jenis pengembalian DoubleFunc. Jika metode Square kedua tidak ada, kesalahan waktu kompilasi akan terjadi.

contoh akhir

12.8.18 Operator typeof

Operator typeof digunakan untuk mendapatkan objek System.Type untuk jenis.

typeof_expression
    : 'typeof' '(' type ')'
    | 'typeof' '(' unbound_type_name ')'
    | 'typeof' '(' 'void' ')'
    ;

unbound_type_name
    : identifier generic_dimension_specifier?
      ('.' identifier generic_dimension_specifier?)*
    | unbound_qualified_alias_member
      ('.' identifier generic_dimension_specifier?)*
    ;

unbound_qualified_alias_member
    : identifier '::' identifier generic_dimension_specifier?
    ;

generic_dimension_specifier
    : '<' comma* '>'
    ;

comma
    : ','
    ;

Bentuk pertama typeof_expression terdiri dari kata kunci typeof diikuti dengan tipe di dalam tanda kurung. Hasil dari ekspresi formulir ini adalah objek System.Type untuk jenis yang ditunjukkan. Hanya ada satu objek System.Type untuk jenis tertentu. Ini berarti bahwa untuk jenis T, typeof(T) == typeof(T) selalu benar. Jenis tidak dapat menjadi dynamic.

Bentuk kedua typeof_expression terdiri dari kata kunci typeof diikuti oleh unbound_type_name dalam tanda kurung.

Catatan: Tata bahasa unbound_type_name dan unbound_qualified_alias_member mengikuti tata bahasa type_name (§7,8) dan qualified_alias_member (§14.8.1) kecuali generic_dimension_specifierdiganti dengan type_argument_list. catatan akhir

Saat mengenali operan typeof_expression jika unbound_type_name dan type_name berlaku, yaitu ketika tidak berisi generic_dimension_specifier atau type_argument_list, maka type_name akan dipilih.

Catatan: ANTLR membuat pilihan yang ditentukan secara otomatis karena pengurutan alternatif typeof_expression. catatan akhir

Arti unbound_type_name ditentukan seolah-olah:

  • Urutan token dikonversi ke type_name dengan mengganti setiap generic_dimension_specifier dengan type_argument_list memiliki jumlah koma dan kata kunci object yang sama dengan setiap type_argument.
  • type_name yang dihasilkan diselesaikan ke tipe terstruktur (§7.8).
  • unbound_type_name kemudian adalah jenis generik yang tidak terikat yang terkait dengan tipe konstruksi terikat yang sudah diselesaikan (§8.4).

Catatan: Tidak ada persyaratan bagi implementasi untuk mengubah urutan token, atau menghasilkan tipe konstruksi perantara; hanya bahwa tipe generik yang tidak terikat yang ditentukan adalah "seolah-olah" proses ini diikuti. catatan akhir

Ini adalah kesalahan jika nama jenis merupakan jenis referensi yang bisa bernilai null.

Hasil dari typeof_expression adalah objek System.Type untuk tipe generik tidak terikat yang dihasilkan.

Bentuk ketiga typeof_expression terdiri dari kata kunci typeof diikuti dengan kata kunci void yang dikurung. Hasil dari ekspresi formulir ini adalah objek System.Type yang mewakili tidak adanya jenis. Objek jenis yang dikembalikan oleh typeof(void) berbeda dari objek jenis yang dikembalikan untuk jenis apa pun.

Catatan: Objek System.Type khusus ini berguna di pustaka kelas yang memungkinkan refleksi pada metode dalam bahasa, di mana metode tersebut membutuhkan cara untuk mewakili jenis pengembalian dari metode mana pun, termasuk metode void, dengan instans System.Type. catatan akhir

Operator typeof dapat digunakan pada parameter jenis. Ini adalah kesalahan waktu kompilasi jika nama jenis diketahui sebagai jenis referensi yang dapat bernilai null. Hasilnya adalah objek System.Type untuk jenis run-time yang terikat ke parameter jenis. Jika jenis run-time adalah jenis referensi nullable, hasilnya adalah jenis referensi non-nullable yang sesuai. Operator typeof juga dapat digunakan pada jenis yang dibangun atau jenis generik yang tidak terikat (§8,4,4). Objek System.Type untuk jenis generik yang tidak terikat tidak sama dengan objek System.Type jenis instans (§15.3.2). Jenis instans selalu merupakan jenis yang dibangun tertutup pada run-time sehingga objek System.Type tergantung pada argumen jenis run-time yang digunakan. Jenis generik yang tidak terikat, di sisi lain, tidak memiliki argumen jenis, dan menghasilkan objek System.Type yang sama terlepas dari argumen jenis runtime.

Contoh: Contoh

class X<T>
{
    public static void PrintTypes()
    {
        Type[] t =
        {
            typeof(int),
            typeof(System.Int32),
            typeof(string),
            typeof(double[]),
            typeof(void),
            typeof(T),
            typeof(X<T>),
            typeof(X<X<T>>),
            typeof(X<>)
        };
        for (int i = 0; i < t.Length; i++)
        {
            Console.WriteLine(t[i]);
        }
    }
}

class Test
{
    static void Main()
    {
        X<int>.PrintTypes();
    }
}

menghasilkan output berikut:

System.Int32
System.Int32
System.String
System.Double[]
System.Void
System.Int32
X`1[System.Int32]
X`1[X`1[System.Int32]]
X`1[T]

Perhatikan bahwa int dan System.Int32 adalah jenis yang sama. Hasil typeof(X<>) tidak bergantung pada argumen jenis tetapi hasil typeof(X<T>) tidak.

contoh akhir

12.8.19 Operator sizeof

Operator sizeof mengembalikan jumlah byte 8-bit yang ditempati oleh variabel dari jenis tertentu. Jenis yang ditentukan sebagai operand ke sizeof adalah unmanaged_type (§8.8).

sizeof_expression
    : 'sizeof' '(' unmanaged_type ')'
    ;

Untuk jenis tertentu yang telah ditentukan sebelumnya, operator sizeof menghasilkan nilai int konstanta seperti yang ditunjukkan pada tabel di bawah ini:

Ekspresi Hasil
sizeof(sbyte) 1
sizeof(byte) 1
sizeof(short) 2
sizeof(ushort) 2
sizeof(int) 4
sizeof(uint) 4
sizeof(long) 8
sizeof(ulong) 8
sizeof(char) 2
sizeof(float) 4
sizeof(double) 8
sizeof(bool) 1
sizeof(decimal) 16

Untuk jenis enum T, hasil ekspresi sizeof(T) adalah nilai konstanta yang sama dengan ukuran jenis dasarnya, seperti yang diberikan di atas. Untuk semua jenis operand lainnya, sizeof operator ditentukan dalam §24.6.9.

12.8.20 Operator yang dicentang dan tidak dicentang

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

checked_expression
    : 'checked' '(' expression ')'
    ;

unchecked_expression
    : 'unchecked' '(' expression ')'
    ;

Operator checked mengevaluasi ekspresi yang terkandung dalam konteks yang dicentang, dan operator unchecked mengevaluasi ekspresi yang terkandung dalam konteks yang tidak dicentang. checked_expression atau unchecked_expression sesuai persis dengan parenthesized_expression (§12.8.5), kecuali bahwa ekspresi yang terkandung dievaluasi dalam konteks pemeriksaan luapan yang diberikan.

Konteks pemeriksaan luapan juga dapat dikontrol melalui pernyataan checked dan unchecked (§13,12).

Operasi berikut dipengaruhi oleh konteks pemeriksaan luapan yang ditetapkan oleh operator dan pernyataan yang diperiksa dan tidak dicentang:

  • Operator dan ++ yang telah -- ditentukan sebelumnya (§12.8.16 dan §12.9.7), ketika operan adalah jenis integral atau enum.
  • Operator unary - yang telah ditentukan sebelumnya (§12,9,3), ketika operand adalah jenis integral.
  • Operator biner , , +-, dan * yang telah /ditentukan sebelumnya (§12.12), ketika kedua operan memiliki jenis integral atau enum.
  • Konversi numerik eksplisit (§10,3,2) dari satu jenis integral atau enum ke jenis integral atau enum lain, atau dari float atau double ke jenis integral atau enum.

Ketika salah satu operasi di atas menghasilkan hasil yang terlalu besar untuk diwakili dalam jenis tujuan, konteks di mana operasi dilakukan mengontrol perilaku yang dihasilkan:

  • checked Dalam konteks, jika operasi adalah ekspresi konstanta (§12,25), terjadi kesalahan waktu kompilasi. Jika tidak, ketika operasi dilakukan pada run-time, System.OverflowException dihasilkan.
  • Dalam konteks unchecked, hasilnya dipotong dengan membuang bit berurutan tinggi yang tidak sesuai dengan jenis tujuan.

Untuk ekspresi non-konstanta (§12,25) (ekspresi yang dievaluasi pada run-time) yang tidak diapit oleh operator atau checked pernyataan apa pununchecked, konteks pemeriksaan luapan default tidak dicentang, kecuali faktor eksternal (seperti sakelar kompilator dan konfigurasi lingkungan eksekusi) memanggil evaluasi yang diperiksa.

Untuk ekspresi konstanta (§12.25) (ekspresi yang dapat dievaluasi sepenuhnya pada waktu kompilasi), konteks pemeriksaan luapan default selalu diperiksa. Kecuali jika ekspresi konstanta ditempatkan secara eksplisit dalam konteks unchecked, overflow yang terjadi selama evaluasi ekspresi pada waktu kompilasi selalu menyebabkan kesalahan kompilasi.

Isi fungsi anonim tidak dipengaruhi oleh konteks checked atau unchecked di mana fungsi anonim terjadi.

Contoh: Dalam kode berikut

class Test
{
    static readonly int x = 1000000;
    static readonly int y = 1000000;

    static int F() => checked(x * y);    // Throws OverflowException
    static int G() => unchecked(x * y);  // Returns -727379968
    static int H() => x * y;             // Depends on default
}

tidak ada kesalahan waktu kompilasi yang dilaporkan karena tidak ada ekspresi yang dapat dievaluasi pada waktu kompilasi. Ketika dijalankan, metode F mengeluarkan System.OverflowException, dan metode G mengembalikan –727379968 (32 bit paling rendah dari hasil yang berada di luar rentang). Perilaku metode H tergantung pada konteks pemeriksaan luapan default untuk kompilasi, tetapi sama dengan F atau sama dengan G.

contoh akhir

Contoh: Dalam kode berikut

class Test
{
    const int x = 1000000;
    const int y = 1000000;

    static int F() => checked(x * y);    // Compile-time error, overflow
    static int G() => unchecked(x * y);  // Returns -727379968
    static int H() => x * y;             // Compile-time error, overflow
}

kelebihan beban yang terjadi saat mengevaluasi ekspresi konstanta dalam F dan H menyebabkan kesalahan pada saat kompilasi dilaporkan, karena ekspresi dievaluasi dalam konteks checked. Luapan juga terjadi saat mengevaluasi ekspresi konstanta dalam G, tetapi karena evaluasi berlangsung dalam konteks unchecked, luapan tidak dilaporkan.

contoh akhir

Operator checked dan unchecked hanya memengaruhi konteks pemeriksaan luapan untuk operasi yang secara tekstual terkandung dalam token "(" dan ")". Operator tidak berpengaruh pada anggota fungsi yang dipanggil sebagai hasil dari mengevaluasi ekspresi yang terkandung.

Contoh: Dalam kode berikut

class Test
{
    static int Multiply(int x, int y) => x * y;

    static int F() => checked(Multiply(1000000, 1000000));
}

penggunaan checked di F tidak memengaruhi evaluasi x * y di Multiply, sehingga x * y dievaluasi dalam konteks pemeriksaan luapan default.

contoh akhir

Operator unchecked nyaman ketika menulis konstanta dari jenis integral yang ditandatangani dalam notasi heksadesimal.

Contoh:

class Test
{
    public const int AllBits = unchecked((int)0xFFFFFFFF);
    public const int HighBit = unchecked((int)0x80000000);
}

Kedua konstanta heksadesimal di atas berjenis uint. Karena konstanta berada di luar rentang int, tanpa operator unchecked, transmisi ke int akan menghasilkan kesalahan waktu kompilasi.

contoh akhir

Catatan: Operator dan pernyataan checked dan unchecked memungkinkan programmer mengontrol aspek tertentu dari beberapa perhitungan numerik. Namun, perilaku beberapa operator numerik tergantung pada jenis data operand mereka. Misalnya, mengalikan dua desimal selalu menghasilkan pengecualian pada kelebihan bahkan dalam konstruksi yang secara eksplisit tidak diperiksa. Demikian pula, mengalikan dua bilangan pecahan tidak pernah menghasilkan pengecualian pada kelebihan muatan bahkan dalam konstruksi yang diperiksa secara eksplisit. Selain itu, operator lain tidak pernah terpengaruh oleh mode pemeriksaan, baik default maupun eksplisit. catatan akhir

12.8.21 Ekspresi nilai bawaan

Ekspresi nilai default digunakan untuk mendapatkan nilai default (§9,3) dari jenis.

default_value_expression
    : explicitly_typed_default
    | default_literal
    ;

explicitly_typed_default
    : 'default' '(' type ')'
    ;

default_literal
    : 'default'
    ;

default_literal mewakili nilai default (§9,3). Ini tidak memiliki jenis, tetapi dapat dikonversi ke jenis apa pun melalui konversi literal default (§10.2.16).

Hasil default_value_expression adalah default (§9,3) dari jenis eksplisit dalam explicitly_typed_default, atau jenis target konversi untuk default_value_expression.

default_value_expression adalah ekspresi konstanta (§12,25) jika jenisnya adalah salah satu dari:

  • tipe referensi
  • parameter jenis yang diketahui sebagai jenis referensi (§8.2);
  • salah satu jenis nilai berikut: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool,; atau
  • jenis enumerasi apa pun.

12.8.22 Alokasi memori tumpukan

Ekspresi alokasi tumpukan mengalokasikan blok memori dari tumpukan eksekusi. Tumpukan eksekusi adalah area memori tempat variabel lokal disimpan. Tumpukan eksekusi bukan bagian dari tumpukan terkelola. Memori yang digunakan untuk penyimpanan variabel lokal secara otomatis dipulihkan ketika fungsi saat ini kembali.

Aturan konteks aman untuk ekspresi alokasi tumpukan dijelaskan dalam §16.4.15.7.

stackalloc_expression
    : 'stackalloc' unmanaged_type '[' expression ']'
    | 'stackalloc' unmanaged_type? '[' constant_expression? ']' stackalloc_initializer
    ;

stackalloc_initializer
     : '{' stackalloc_initializer_element_list '}'
     ;

stackalloc_initializer_element_list
     : stackalloc_element_initializer (',' stackalloc_element_initializer)* ','?
     ;
    
stackalloc_element_initializer
    : expression
    ;

unmanaged_type (§8,8) menunjukkan jenis item yang akan disimpan di lokasi yang baru dialokasikan, dan ekspresi menunjukkan jumlah item ini. Diambil bersama-sama, ini menentukan ukuran alokasi yang diperlukan. Jenis ekspresi harus secara implisit dapat dikonversi ke jenis int.

Karena ukuran alokasi tumpukan tidak boleh negatif, akan terjadi kesalahan pada waktu kompilasi untuk menentukan jumlah item sebagai constant_expression yang menghasilkan nilai negatif.

Pada runtime jika jumlah item yang akan dialokasikan adalah nilai negatif maka perilaku tidak ditentukan. Jika nol, maka tidak ada alokasi yang dibuat, dan nilai yang dikembalikan ditentukan implementasi. Jika tidak ada cukup memori yang tersedia untuk mengalokasikan item, maka terjadi kesalahan System.StackOverflowException.

Saat ada stackalloc_initializer:

  • Jika unmanaged_type dihilangkan, aturan disimpulkan mengikuti aturan untuk jenis umum terbaik (§12.6.3.16) untuk kumpulan stackalloc_element_initializer.
  • Jika constant_expression dihilangkan, maka dianggap sebagai jumlah dari stackalloc_element_initializer.
  • Jika constant_expression hadir, maka jumlahnya harus sama dengan jumlah stackalloc_element_initializer.

Setiap stackalloc_element_initializer akan memiliki konversi implisit ke unmanaged_type (§10,2). Inisialisasi elemen stackalloc_element_initializerdalam memori yang telah dialokasikan dilakukan dalam urutan menaik, dimulai dari elemen pada indeks nol. Dengan tidak adanya stackalloc_initializer, konten memori yang baru dialokasikan tidak terdefinisi.

Jika stackalloc_expression terjadi secara langsung sebagai ekspresi inisialisasi local_variable_declaration (§13.6.2), di mana local_variable_type adalah jenis penunjuk (§24,3) atau disimpulkan (var), maka hasil stackalloc_expression adalah penunjuk jenis T* (§24,9). Dalam hal ini stackalloc_expression harus muncul dalam kode yang tidak aman. Jika tidak, hasil dari stackalloc_expression adalah sebuah instans dari jenis Span<T>, di mana T merupakan unmanaged_type:

  • Span<T> (§C.3) adalah tipe struct ref (§16.2.3), yang merepresentasikan blok memori, di sini blok yang dialokasikan oleh stackalloc_expression, sebagai koleksi yang dapat diindeks dari item bertipe (T).
  • Properti Length dari hasil mengembalikan jumlah item yang dialokasikan.
  • Pengindeks hasil (§15.9) mengembalikan variable_reference (§9.5) ke item dari blok yang dialokasikan dan diperiksa rentangnya.

Inisialisasi alokasi memori tumpukan tidak diizinkan dalam blok catch atau finally (§13.11).

Catatan: Tidak ada cara untuk secara eksplisit membebaskan memori yang dialokasikan menggunakan stackalloc. catatan akhir

Semua blok memori yang dialokasikan pada tumpukan yang dibuat selama eksekusi anggota fungsi secara otomatis dibuang ketika anggota fungsi tersebut selesai.

Kecuali untuk operator stackalloc, C# tidak menyediakan konstruksi yang telah ditentukan sebelumnya untuk mengelola memori yang tidak dikumpulkan sampah. Layanan tersebut biasanya disediakan oleh pustaka kelas pendukung atau diimpor langsung dari sistem operasi yang mendasarinya.

Contoh:

// Memory uninitialized
Span<int> span1 = stackalloc int[3];
// Memory initialized
Span<int> span2 = stackalloc int[3] { -10, -15, -30 };
// Type int is inferred
Span<int> span3 = stackalloc[] { 11, 12, 13 };
// Error; result is int*, not allowed in a safe context
var span4 = stackalloc[] { 11, 12, 13 };
// Error; no conversion from Span<int> to Span<long>
Span<long> span5 = stackalloc[] { 11, 12, 13 };
// Converts 11 and 13, and returns Span<long> 
Span<long> span6 = stackalloc[] { 11, 12L, 13 };
// Converts all and returns Span<long>
Span<long> span7 = stackalloc long[] { 11, 12, 13 };
// Implicit conversion of Span<T>
ReadOnlySpan<int> span8 = stackalloc int[] { 10, 22, 30 };
// Implicit conversion of Span<T>
Widget<double> span9 = stackalloc double[] { 1.2, 5.6 };

public class Widget<T>
{
    public static implicit operator Widget<T>(Span<double> sp) { return null; }
}

Dalam kasus span8, stackalloc menghasilkan Span<int>, yang dikonversi oleh operator implisit ke ReadOnlySpan<int>. Demikian pula, untuk span9, Span<double> yang dihasilkan dikonversi ke jenis yang ditentukan pengguna Widget<double> menggunakan konversi, seperti yang ditunjukkan. contoh akhir

12.8.23 Operator nameof

nameof_expression digunakan untuk mendapatkan nama entitas program sebagai string konstanta.

nameof_expression
    : 'nameof' '(' named_entity ')'
    ;
    
named_entity
    : named_entity_target ('.' identifier type_argument_list?)*
    ;
    
named_entity_target
    : simple_name
    | 'this'
    | 'base'
    | predefined_type 
    | qualified_alias_member
    ;

Karena nameof bukan kata kunci, nameof_expression selalu ambigu secara sintaksis dengan pemanggilan nama sederhana nameof. Untuk alasan kompatibilitas, jika pencarian nama (§12.8.4) terhadap nama nameof berhasil, ekspresi tersebut diperlakukan sebagai pemanggilan ekspresi (invocation_expression) — tidak peduli apakah pemanggilan itu valid atau tidak. Jika tidak demikian, itu adalah nameof_expression.

Pencarian akses anggota dan nama sederhana dilakukan pada named_entity pada waktu kompilasi, mengikuti aturan yang dijelaskan dalam §12.8.4 dan §12.8.7. Namun, di mana pencarian yang dijelaskan dalam §12.8.4 dan §12.8.7 menghasilkan kesalahan karena anggota instans ditemukan dalam konteks statis, nameof_expression tidak menghasilkan kesalahan tersebut.

Terjadi kesalahan pada waktu kompilasi jika named_entity yang menunjuk ke sekelompok metode memiliki type_argument_list. Ini adalah kesalahan waktu kompilasi untuk named_entity_target memiliki jenis dynamic.

nameof_expression adalah ekspresi konstan tipe string, dan tidak memiliki efek saat runtime. Secara khusus, named_entity tidak dievaluasi, dan diabaikan untuk tujuan analisis penetapan pasti (§9.4.4.22). Nilainya adalah pengidentifikasi terakhir dari named_entity sebelum type_argument_list opsional terakhir, diubah dengan cara berikut:

  • Awalan "@", jika digunakan, dihapus.
  • Setiap unicode_escape_sequence diubah menjadi karakter Unicode yang sesuai.
  • Setiap karakter pemformatan dihapus.

Ini adalah transformasi yang sama yang diterapkan dalam §6.4.3 saat menguji kesetaraan antara pengidentifikasi.

Contoh: Berikut ini menggambarkan hasil berbagai ekspresi nameof, dengan asumsi jenis generik List<T> dideklarasikan dalam namespace System.Collections.Generic:

using TestAlias = System.String;

class Program
{
    static void Main()
    {
        var point = (x: 3, y: 4);

        string n1 = nameof(System);                      // "System"
        string n2 = nameof(System.Collections.Generic);  // "Generic"
        string n3 = nameof(point);                       // "point"
        string n4 = nameof(point.x);                     // "x"
        string n5 = nameof(Program);                     // "Program"
        string n6 = nameof(System.Int32);                // "Int32"
        string n7 = nameof(TestAlias);                   // "TestAlias"
        string n8 = nameof(List<int>);                   // "List"
        string n9 = nameof(Program.InstanceMethod);      // "InstanceMethod"
        string n10 = nameof(Program.GenericMethod);      // "GenericMethod"
        string n11 = nameof(Program.NestedClass);        // "NestedClass"

        // Invalid
        // string x1 = nameof(List<>);            // Empty type argument list
        // string x2 = nameof(List<T>);           // T is not in scope
        // string x3 = nameof(GenericMethod<>);   // Empty type argument list
        // string x4 = nameof(GenericMethod<T>);  // T is not in scope
        // string x5 = nameof(int);               // Keywords not permitted
        // Type arguments not permitted for method group
        // string x6 = nameof(GenericMethod<Program>);
    }

    void InstanceMethod() { }

    void GenericMethod<T>()
    {
        string n1 = nameof(List<T>); // "List"
        string n2 = nameof(T);       // "T"
    }

    class NestedClass { }
}

Bagian yang mungkin mengejutkan dari contoh ini adalah penyelesaian pada nameof(System.Collections.Generic) menjadi "Generik" ketimbang namespace lengkap, dan pada nameof(TestAlias) menjadi "TestAlias" bukan "String". contoh akhir

12.8.24 Pernyataan metode anonim

anonymous_method_expression adalah salah satu dari dua cara untuk menentukan fungsi anonim. Ini dijelaskan lebih lanjut dalam §12.21.

12.9 Operator Unary

12.9.1 Umum

operator +, , -!(negasi logis §12.9.4 saja), ~, , ^, ++--, cast, dan await disebut operator unary.

Note: Operator postfix null-forgiving (§12.8.9), !, karena sifatnya yang hanya dapat digunakan pada waktu kompilasi dan tidak dapat di-overload, dikecualikan dari daftar di atas. catatan akhir

unary_expression
    : primary_expression
    | '+' unary_expression
    | '-' unary_expression
    | logical_negation_operator unary_expression
    | '~' unary_expression
    | '^' unary_expression
    | pre_increment_expression
    | pre_decrement_expression
    | cast_expression
    | await_expression
    | pointer_indirection_expression    // unsafe code support
    | addressof_expression              // unsafe code support
    ;

pointer_indirection_expression (§24.6.2) dan addressof_expression (§24.6.5) hanya tersedia dalam kode yang tidak aman (§24).

Jika operan unary_expression memiliki jenis waktu kompilasi dynamic, itu terikat secara dinamis (§12.3.3). Dalam hal ini:

  • jenis waktu kompilasi unary_expression adalah:
    • Index ^ untuk operator dari akhir indeks (§12.9.6)
    • dynamic untuk semua operator unary lainnya; dan
  • resolusi yang dijelaskan di bawah ini akan berlangsung pada run-time menggunakan jenis run-time operand.

12.9.2 Operator unary plus

Untuk pengoperasian formulir +x, resolusi kelebihan beban operator unary (§12.4.4) diterapkan untuk memilih implementasi operator tertentu. Operand dikonversi ke tipe parameter dari operator yang dipilih, dan tipe hasilnya adalah tipe pengembalian dari operator. Operator unary plus yang telah ditentukan sebelumnya adalah:

int operator +(int x);
uint operator +(uint x);
long operator +(long x);
ulong operator +(ulong x);
float operator +(float x);
double operator +(double x);
decimal operator +(decimal x);

Untuk masing-masing operator ini, hasilnya hanyalah nilai operand.

Bentuk angkat (§12.4.8) dari operator unary plus yang ditentukan di atas juga telah ditentukan sebelumnya.

Operator unari minus

Untuk pengoperasian formulir –x, resolusi kelebihan beban operator unary (§12.4.4) diterapkan untuk memilih implementasi operator tertentu. Operand dikonversi ke tipe parameter dari operator yang dipilih, dan tipe hasilnya adalah tipe pengembalian dari operator. Operator minus unari yang telah ditentukan sebelumnya adalah:

  • Negasi bilangan bulat:

    int operator –(int x);
    long operator –(long x);
    

    Hasilnya dihitung dengan mengurangi X dari nol. Jika nilai X adalah nilai terkecil yang dapat diwakili dari jenis operand (−2³¹ untuk int atau −2⁶³ untuk long), maka negasi matematika X tidak dapat diwakili dalam jenis operand. Jika ini terjadi dalam konteks checked, System.OverflowException akan dilemparkan; jika terjadi dalam konteks unchecked, hasilnya adalah nilai operand dan luapan tidak dilaporkan.

    Jika operan operator negasi berjenis uint, itu dikonversi ke jenis long, dan jenis hasilnya long. Pengecualian adalah aturan yang mengizinkan nilai int−2147483648 (−2³¹) ditulis sebagai literal bilangan bulat desimal (§6.4.5.3).

    Jika operan operator negasi berjenis ulong, kesalahan waktu kompilasi terjadi. Pengecualian adalah aturan yang mengizinkan nilai long−9223372036854775808 (−2⁶³) ditulis sebagai literal bilangan bulat desimal (§6.4.5.3)

  • Negasi titik mengambang:

    float operator –(float x);
    double operator –(double x);
    

    Hasilnya adalah nilai X dengan tanda terbalik. Jika x adalah NaN, maka hasilnya juga NaN.

  • Negasi yang desimal

    decimal operator –(decimal x);
    

    Hasilnya dihitung dengan mengurangi X dari nol. Negasi dari bilangan desimal setara dengan menggunakan operator minus unar dari jenis System.Decimal.

Bentuk terangkat (§12.4.8) dari operator minus unary tidak terangkat yang telah ditentukan di atas juga sudah ditentukan sebelumnya.

12.9.4 Operator negasi logis

Untuk pengoperasian formulir !x, resolusi kelebihan beban operator unary (§12.4.4) diterapkan untuk memilih implementasi operator tertentu. Operand dikonversi ke tipe parameter dari operator yang dipilih, dan tipe hasilnya adalah tipe pengembalian dari operator. Hanya satu operator negasi logis yang telah ditentukan sebelumnya yang ada:

bool operator !(bool x);

Operator ini menghitung negasi logis operand: Jika operand true, hasilnya false. Jika operand adalah false, maka hasilnya adalah true.

Bentuk angkat (§12.4.8) dari operator negasi logis yang telah ditentukan di atas juga telah ditentukan sebelumnya.

Catatan: Operator negasi logis awalan dan operator pemaaf null postfix (§12.8.9), meskipun diwakili oleh token leksikal yang sama (!), adalah berbeda. catatan akhir

12.9.5 Operator pelengkap Bitwise

Untuk pengoperasian formulir ~x, resolusi kelebihan beban operator unary (§12.4.4) diterapkan untuk memilih implementasi operator tertentu. Operand dikonversi ke tipe parameter dari operator yang dipilih, dan tipe hasilnya adalah tipe pengembalian dari operator. Operator pelengkap bitwise yang telah ditentukan sebelumnya adalah:

int operator ~(int x);
uint operator ~(uint x);
long operator ~(long x);
ulong operator ~(ulong x);

Untuk masing-masing operator ini, hasil operasi adalah komplemen bitwise dari x.

Setiap jenis enumerasi E secara implisit menyediakan operator pelengkap bitwise berikut:

E operator ~(E x);

Hasil mengevaluasi ~x, di mana X adalah ekspresi dari jenis enumerasi E dengan jenis Uyang mendasar , sama persis dengan mengevaluasi (E)(~(U)x), kecuali bahwa konversi ke E selalu dilakukan seolah-olah dalam konteks unchecked (§12.8.20).

Bentuk angkat (§12.4.8) dari operator pelengkap bitwise yang sudah ditentukan sebelumnya di atas juga telah ditentukan.

12.9.6 Indeks operator dari akhir

Operator unary ^ disebut operator indeks dari ujung (yang secara kolektial disebut sebagai operator topi). Operator ini tidak dapat kelebihan beban (§12.4.3) dan ada satu operator yang telah ditentukan sebelumnya:

Index operator ^(int x);

Hasil operasi formulir ^x adalah nilai dari ujung Index (§18,2) yang setara dengan hasil ekspresi:

new Index(x, true)

Seperti halnya unary_expressionlain, operan mungkin memiliki jenis waktu kompilasi dynamic (§12.9.1) dan terikat secara dinamis (§12.3.3). Jenis waktu kompilasi hasil selalu Index.

Bentuk indeks yang diangkat (§12.4.8) dari operator dari ujung juga telah ditentukan sebelumnya.

12.9.7 Prefiks kenaikan dan operator penurunan

pre_increment_expression
    : '++' unary_expression
    ;

pre_decrement_expression
    : '--' unary_expression
    ;

Operasi penambahan atau pengurangan prefiks harus berupa ekspresi yang diklasifikasikan sebagai variabel, akses properti, atau akses pengindeks. Hasil operasi adalah nilai dengan jenis yang sama dengan operand.

Jika operan dari operasi kenaikan atau penurunan prefiks adalah akses properti atau pengindeks, properti atau pengindeks tersebut harus memiliki kedua aksesor, yaitu get dan set. Jika ini tidak terjadi, kesalahan waktu pengikatan terjadi.

Resolusi kelebihan beban operator unary (§12.4.4) diterapkan untuk memilih implementasi operator tertentu. Operator ++ dan -- yang telah ditentukan sebelumnya ada untuk jenis berikut: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, dan jenis enum apa pun. Operator ++ yang telah ditentukan sebelumnya mengembalikan nilai yang dihasilkan dengan menambahkan 1 ke operand, dan operator -- yang telah ditentukan sebelumnya mengembalikan nilai yang dihasilkan dengan mengurangi 1 dari operand. Dalam konteks checked, jika hasil penambahan atau pengurangan ini berada di luar rentang jenis hasil dan jenis hasilnya adalah jenis integral atau jenis enum, System.OverflowException dilemparkan.

Harus ada konversi implisit dari jenis pengembalian operator unary yang dipilih ke jenis unary_expression, jika tidak, kesalahan waktu kompilasi terjadi.

Pemrosesan run-time dari operasi kenaikan awalan atau penurunan formulir ++x atau --x terdiri dari langkah-langkah berikut:

  • Jika x diklasifikasikan sebagai variabel:
    • x dievaluasi untuk menghasilkan variabel.
    • Nilai x dikonversi ke jenis operand operator yang dipilih dan operator dipanggil dengan nilai ini sebagai argumennya.
    • Nilai yang dikembalikan oleh operator dikonversi ke jenis x. Nilai yang dihasilkan disimpan di lokasi yang diberikan oleh evaluasi x dan menjadi hasil operasi.
  • Jika x diklasifikasikan sebagai akses properti atau pengindeks:
    • Ekspresi instans (jika x tidak static) dan daftar argumen (jika x adalah akses pengindeks) yang terkait dengan x dievaluasi, dan hasilnya digunakan dalam pemanggilan get dan set aksesor berikutnya.
    • Aksesor get x dipanggil.
    • Nilai yang dikembalikan oleh aksesor get dikonversi ke jenis operand operator dan operator yang dipilih dipanggil dengan nilai ini sebagai argumennya.
    • Nilai yang dikembalikan oleh operator dikonversi ke jenis x. Aksesor set x dipanggil dengan nilai ini sebagai argumen nilainya.
    • Nilai ini juga merupakan hasil operasi.

Operator ++ dan -- juga mendukung notasi postfix (§12.8.16). Hasil x++ atau x-- adalah nilai x sebelum operasi, sedangkan hasil ++x atau --x adalah nilai x setelah operasi. Dalam kedua kasus, x itu sendiri memiliki nilai yang sama setelah operasi.

Penerapan operator ++ atau operator -- dapat dipanggil menggunakan notasi postfix atau prefiks. Tidak dimungkinkan untuk memiliki implementasi operator terpisah untuk dua notasi tersebut.

Bentuk yang diangkat (§12.4.8) dari operator prefiks kenaikan dan penurunan yang tidak diangkat yang telah didefinisikan sebelumnya juga telah dipredefinisikan.

12.9.8 Ekspresi pemeran

cast_expression digunakan untuk secara eksplisit mengonversi suatu ekspresi ke jenis tertentu.

cast_expression
    : '(' type ')' unary_expression
    ;

cast_expression dari bentuk (T)E, di mana T adalah tipe dan E adalah unary_expression, melaksanakan konversi eksplisit (§10.3) dari nilai E ke tipe T. Jika tidak ada konversi eksplisit dari E ke T, kesalahan waktu pengikatan terjadi. Jika tidak, hasilnya adalah nilai yang dihasilkan oleh konversi eksplisit. Hasilnya selalu diklasifikasikan sebagai nilai, bahkan jika E menunjukkan variabel.

Tata bahasa untuk cast_expression menyebabkan ambiguitas sinaktik tertentu.

Contoh: Ekspresi (x)–y dapat ditafsirkan sebagai cast_expression (pemeran –y untuk mengetik x) atau sebagai additive_expression dikombinasikan dengan parenthesized_expression (yang menghitung nilai x – y). contoh akhir

Untuk mengatasi ambiguitas cast_expression, aturan berikut ada: Urutan satu atau beberapa token (§6,4) yang diapit dalam tanda kurung dianggap sebagai awal cast_expression hanya jika setidaknya salah satu hal berikut ini benar:

  • Urutan token adalah tata bahasa yang benar untuk jenis, tetapi tidak untuk ekspresi.
  • Urutan token adalah tata bahasa yang benar untuk tipe, dan token yang langsung setelah tanda kurung tutup adalah token "~", token "!", token "(", identifikasi (§6.4.3), literal (§6.4.5), atau kata kunci apa pun (§6.4.4) kecuali as dan is.

Istilah "tata bahasa yang benar" di atas hanya berarti bahwa urutan token harus sesuai dengan produksi tata bahasa tertentu. Ini secara khusus tidak mempertimbangkan arti aktual dari pengidentifikasi konstituen apa pun.

Contoh: Jika x dan y adalah pengidentifikasi, maka x.y adalah tata bahasa yang benar untuk jenis, bahkan jika x.y tidak benar-benar menunjukkan jenis. contoh akhir

Catatan: Dari aturan disambiguasi, dapat disimpulkan bahwa, jika x dan y adalah pengidentifikasi, maka (x)y, (x)(y), dan (x)(-y)cast_expression, tetapi (x)-y tidak, walaupun x mengidentifikasi jenis. Namun, jika x adalah kata kunci yang mengidentifikasi jenis yang telah ditentukan sebelumnya (seperti int), maka keempat formulir adalah cast_expression(karena kata kunci seperti itu tidak mungkin bisa menjadi ekspresi dengan sendirinya). catatan akhir

12.9.9 Menunggu ekspresi

12.9.9.1 Umum

Operator await digunakan untuk menangguhkan evaluasi fungsi async yang melingkupi hingga operasi asinkron yang diwakili oleh operand telah selesai.

await_expression
    : 'await' unary_expression
    ;

Await_expression hanya diperbolehkan dalam isi fungsi asinkron (§15.14). Dalam fungsi penutup asinkron terdekat, await_expression tidak boleh muncul di tempat-tempat ini:

  • Di dalam fungsi anonim berlapis (non-asinkron)
  • Di dalam blok dari pernyataan penguncian lock_statement
  • Dalam konversi fungsi anonim ke jenis pohon ekspresi (§10.7.3)
  • Dalam konteks yang tidak aman

Catatan: await_expression tidak dapat terjadi di sebagian besar tempat dalam query_expression, karena secara sintaks diubah untuk menggunakan ekspresi lambda yang tidak asinkron. catatan akhir

Di dalam fungsi asinkron, await tidak boleh digunakan sebagai available_identifier meskipun pengidentitas verbatim @await dapat digunakan. Oleh karena itu tidak ada ambiguitas sintaks antara await_expressiondan berbagai ekspresi yang melibatkan identifikasi. Di luar fungsi asinkron, await bertindak sebagai pengidentifikasi normal.

Operan await_expression disebut tugas. Ini mewakili operasi asinkron yang mungkin atau mungkin tidak selesai pada saat await_expression dievaluasi. Tujuan operator await adalah untuk menangguhkan eksekusi fungsi asinkron yang melingkupi hingga tugas yang sedang ditunggu selesai, dan kemudian memperoleh hasilnya.

12.9.9.2 Ekspresi yang dapat ditunggu

Tugas await_expression harus dapat ditunggu. Ekspresi t dapat ditunggu jika salah satu hal berikut ini berlaku:

  • t adalah jenis pada saat kompilasi dynamic
  • t memiliki instans atau metode ekstensi yang dapat diakses yang disebut GetAwaiter tanpa parameter dan tanpa parameter jenis, dan jenis pengembalian A untuk mana semua hal berikut berlaku:
    • A mengimplementasikan antarmuka System.Runtime.CompilerServices.INotifyCompletion (selanjutnya dikenal sebagai INotifyCompletion untuk singkat)
    • A memiliki properti instans IsCompleted yang dapat diakses dan mudah dibaca bertipe bool
    • A memiliki metode instans yang dapat diakses GetResult tanpa parameter dan tidak ada parameter jenis

Tujuan dari metode GetAwaiter adalah untuk mendapatkan awaiter untuk tugas tersebut. Tipe A disebut tipe penunggu untuk ekspresi menunggu.

Tujuan properti IsCompleted adalah untuk menentukan apakah tugas sudah selesai. Jika demikian, tidak perlu menangguhkan evaluasi.

Tujuan dari metode INotifyCompletion.OnCompleted adalah untuk mendaftarkan "kelanjutan" untuk tugas; yaitu, delegasi (tipe System.Action) yang akan dipanggil setelah tugas selesai.

Tujuan dari metode GetResult adalah untuk mendapatkan hasil tugas setelah selesai. Hasil ini mungkin berupa keberhasilan penyelesaian, mungkin dengan nilai hasil, atau mungkin merupakan pengecualian yang dilemparkan oleh metode GetResult.

12.9.9.3 Klasifikasi ekspresi tunggu

Ekspresi await t diklasifikasikan dengan cara yang sama seperti ekspresi (t).GetAwaiter().GetResult(). Dengan demikian, jika jenis pengembalian dari GetResult adalah void, await_expression dikategorikan sebagai tidak berarti. Jika memiliki tipe pengembalian non-voidT, await_expression diklasifikasikan sebagai nilai dengan tipe T.

12.9.9.4 Evaluasi run-time ekspresi tunggu

Pada waktu proses, ekspresi await t dievaluasi sebagai berikut:

  • Suatu a awaiter diperoleh dengan mengevaluasi ekspresi (t).GetAwaiter().
  • bool b diperoleh dengan mengevaluasi ekspresi (a).IsCompleted.
  • Jika bfalse maka evaluasi bergantung pada apakah a menggunakan antarmuka System.Runtime.CompilerServices.ICriticalNotifyCompletion (selanjutnya dikenal sebagai ICriticalNotifyCompletion untuk singkatnya). Pemeriksaan ini dilakukan pada waktu pengikatan; yaitu, pada run-time jika a memiliki jenis waktu kompilasi dynamic, dan pada waktu kompilasi jika tidak. Biarkan r menunjukkan delegasi kelanjutan (§15.14):
    • Jika a tidak menerapkan ICriticalNotifyCompletion, ekspresi ((a) as INotifyCompletion).OnCompleted(r) dievaluasi.
    • Jika a menerapkan ICriticalNotifyCompletion, ekspresi ((a) as ICriticalNotifyCompletion).UnsafeOnCompleted(r) dievaluasi.
    • Evaluasi kemudian ditangguhkan, dan kontrol dikembalikan ke pemanggil fungsi asinkron saat ini.
  • Entah segera setelah (jika b adalah true), atau setelah pemanggilan delegasi pelanjutan nanti (jika b adalah false), ekspresi (a).GetResult() dievaluasi. Jika mengembalikan nilai, nilai tersebut adalah hasil dari await_expression. Jika tidak, hasilnya tidak ada.

Implementasi awaiter dari metode antarmuka INotifyCompletion.OnCompleted dan ICriticalNotifyCompletion.UnsafeOnCompleted harus menyebabkan delegasi r dipanggil tidak lebih dari sekali. Jika tidak, perilaku fungsi asinkron yang mencakup tidak terdefinisi.

Operator Rentang 12.10

Operator .. disebut operator rentang .

range_expression
    : unary_expression
    | unary_expression? '..' unary_expression?
    ;

Operator rentang yang telah ditentukan sebelumnya adalah:

Range operator ..(Index x, Index y);

Operator rentang tidak dapat kelebihan beban (§12.4.3).

Semua ekspresi rentang diperlakukan sebagai memiliki formulir x..y, di mana:

  • x adalah operan kiri jika ada, jika tidak, ekspresi 0; dan
  • y adalah operan yang tepat jika ada, jika tidak, ekspresi ^0.

Hasil operasi adalah Range nilai (§18,3) yang setara dengan hasil ekspresi:

new Range(x, y)

Jika salah satu atau kedua operand dalam ekspresi rentang memiliki jenis waktu kompilasi dynamic, maka ekspresi terikat secara dinamis (§12.3.3). Jenis waktu kompilasi hasil selalu Range.

Bentuk operator rentang yang diangkat (§12.4.8) juga telah ditentukan sebelumnya.

Operator rentang tidak asosiatif (§12.4.2).

12.11 Beralih ekspresi

switch_expression menyediakan switchsemantik -seperti dalam konteks ekspresi.

switch_expression
    : range_expression
    | switch_expression 'switch' '{' switch_expression_arms? '}'
    ;

switch_expression_arms
    : switch_expression_arm (',' switch_expression_arm)* ','?
    ;

switch_expression_arm
    : pattern case_guard? '=>' switch_expression_arm_expression
    ;

switch_expression_arm_expression
    : expression
    ;

Ada konversi ekspresi pengalihan (§10.2.18) dari ekspresi pengalihan ke jenis T jika ada konversi implisit dari setiap switch_expression_arm_expression setiap switch_expression_armekspresi pengalihan ke T.

Jika ekspresi pengalihan tidak tunduk pada konversi ekspresi pengalihan, maka

  • Jenis switch_expression adalah jenis umum terbaik §12.6.3.16) dari switch_expression_arm_expressiondari switch_expression_arm, jika jenis tersebut ada, dan setiap switch_expression_arm_expression dapat dikonversi secara implisit ke jenis tersebut.
  • Ini adalah kesalahan jika tidak ada jenis seperti itu.

Ini adalah kesalahan jika beberapa pola switch_expression_arm tidak dapat memengaruhi hasil karena beberapa pola dan penjaga sebelumnya akan selalu cocok.

Ekspresi pengalih dikatakan lengkap jika setiap nilai inputnya ditangani oleh setidaknya satu lengan ekspresi sakelar. Pengkompilasi akan menghasilkan peringatan jika ekspresi pengalihan tidak lengkap. Pada runtime, hasil switch_expression adalah nilai ekspresiswitch_expression_arm pertama di mana ekspresi di sisi kiri switch_expression cocok dengan pola switch_expression_arm, dan yang case_guardswitch_expression_arm, jika ada, mengevaluasi ke true. Jika tidak ada switch_expression_arm seperti itu, switch_expression melempar instans pengecualian System.InvalidOperationException (atau kelas yang berasal dari itu).

Contoh: Berikut ini mengonversi nilai enum yang mewakili arah visual pada peta online ke arah kardinal yang sesuai:

static Orientation ToOrientation(Direction direction) => direction switch
{
    Direction.Up    => Orientation.North,
    Direction.Right => Orientation.East,
    Direction.Down  => Orientation.South,
    Direction.Left  => Orientation.West,
    _ => throw new ArgumentOutOfRangeException(direction.ToString()),
};

public enum Direction
{
    Up,
    Down,
    Right,
    Left
}

public enum Orientation
{
    North,
    South,
    East,
    West
}

contoh akhir

12.12 Operator aritmatika

12.12.1 Umum

Operator *, /, %, +, dan - disebut operator aritmatika.

multiplicative_expression
    : switch_expression
    | multiplicative_expression '*' switch_expression
    | multiplicative_expression '/' switch_expression
    | multiplicative_expression '%' switch_expression
    ;

additive_expression
    : multiplicative_expression
    | additive_expression '+' multiplicative_expression
    | additive_expression '-' multiplicative_expression
    ;

Jika operan operator aritmatika memiliki jenis waktu kompilasi dynamic, maka ekspresi terikat secara dinamis (§12.3.3). Dalam hal ini, tipe kompilasi-waktu ekspresi adalah dynamic, dan resolusi yang dijelaskan di bawah ini akan berlangsung pada run-time menggunakan tipe run-time dari operand yang memiliki tipe kompilasi-waktu dynamic.

12.12.2 Operator perkalian

Untuk pengoperasian formulir x * y, resolusi kelebihan beban operator biner (§12.4.5) diterapkan untuk memilih implementasi operator tertentu. Operand dikonversi ke jenis parameter operator yang dipilih, dan jenis hasilnya adalah jenis pengembalian operator.

Operator perkalian yang telah ditentukan sebelumnya tercantum di bawah ini. Semua operator menghitung hasil kali dari x dan y.

  • Perkalian bilangan bulat:

    int operator *(int x, int y);
    uint operator *(uint x, uint y);
    long operator *(long x, long y);
    ulong operator *(ulong x, ulong y);
    

    Dalam konteks checked, jika produk berada di luar rentang jenis hasil, System.OverflowException dilemparkan. Dalam konteks unchecked, luapan tidak dilaporkan dan bit urutan tinggi yang signifikan yang berada di luar rentang tipe hasil diabaikan.

  • Perkalian titik mengambang:

    float operator *(float x, float y);
    double operator *(double x, double y);
    

    Produk ini dihitung sesuai dengan aturan aritmatika IEC 60559. Tabel berikut mencantumkan hasil semua kemungkinan kombinasi nilai terbatas nonzero, nol, tak terbatas, dan NaN. Dalam tabel, x dan y adalah nilai terbatas positif. z adalah hasil x * y, dibulatkan ke nilai terdekat yang dapat diwakili. Jika besarnya hasil terlalu besar untuk jenis tujuan, z tidak terbatas. Karena pembulatan, z mungkin nol meskipun baik x maupun y tidak nol.

    +y -y +0 -0 +∞ -∞ NaN
    +x +z -z +0 -0 +∞ -∞ NaN
    -x -z +z -0 +0 -∞ +∞ NaN
    +0 +0 -0 +0 -0 NaN NaN NaN
    -0 -0 +0 -0 +0 NaN NaN NaN
    +∞ +∞ -∞ NaN NaN +∞ -∞ NaN
    -∞ -∞ +∞ NaN NaN -∞ +∞ NaN
    NaN NaN NaN NaN NaN NaN NaN NaN

    (Kecuali jika dinyatakan lain, dalam tabel floating-point dalam §12.12.2§12.12.6 penggunaan "+" berarti nilainya positif; penggunaan "-" berarti nilai negatif; dan kurangnya tanda berarti nilainya mungkin positif atau negatif atau tidak memiliki tanda (NaN).)

  • Perkalian desimal:

    decimal operator *(decimal x, decimal y);
    

    Jika besarnya nilai yang dihasilkan terlalu besar untuk diwakili dalam format desimal, System.OverflowException dilemparkan. Karena pembulatan, hasilnya bisa nol meskipun tak ada operan yang nol. Skala hasil adalah penjumlahan dari skala dua operan sebelum pembulatan apa pun. Perkalian desimal setara dengan menggunakan operator perkalian jenis System.Decimal.

Bentuk yang diangkat (§12.4.8) dari operator perkalian yang telah ditentukan sebelumnya juga telah ditentukan.

12.12.3 Operator divisi

Untuk pengoperasian formulir x / y, resolusi kelebihan beban operator biner (§12.4.5) diterapkan untuk memilih implementasi operator tertentu. Operand dikonversi ke jenis parameter operator yang dipilih, dan jenis hasilnya adalah jenis pengembalian operator.

Operator divisi yang telah ditentukan sebelumnya tercantum di bawah ini. Semua operator menghitung hasil bagi dari x dan y.

  • Pembagian bilangan bulat:

    int operator /(int x, int y);
    uint operator /(uint x, uint y);
    long operator /(long x, long y);
    ulong operator /(ulong x, ulong y);
    

    Jika nilai operand kanan adalah nol, System.DivideByZeroException akan dilemparkan.

    Pembagian membulatkan hasil menuju nol. Dengan demikian nilai absolut dari hasil adalah bilangan bulat terbesar yang mungkin kurang dari atau sama dengan nilai absolut dari kuota dua operan. Hasilnya adalah nol atau positif ketika kedua operan memiliki tanda yang sama dan nol atau negatif ketika kedua operand memiliki tanda yang berlawanan.

    Jika operand kiri adalah nilai int atau long terkecil yang dapat diwakili dan operand kanan –1, terjadi luapan. Dalam konteks checked, ini menyebabkan System.ArithmeticException (atau subkelasnya) dilemparkan. Dalam konteks unchecked, itu ditentukan oleh implementasi apakah System.ArithmeticException (atau subkelasnya) dilemparkan atau luapan yang tidak dilaporkan dengan nilai yang dihasilkan adalah nilai dari operand kiri.

  • Pembagian floating-point:

    float operator /(float x, float y);
    double operator /(double x, double y);
    

    Kuota dihitung sesuai dengan aturan aritmatika IEC 60559. Tabel berikut mencantumkan hasil semua kemungkinan kombinasi nilai terbatas nonzero, nol, tak terbatas, dan NaN. Dalam tabel, x dan y adalah nilai terbatas positif. z adalah hasil x / y, dibulatkan ke nilai terdekat yang dapat diwakili.

    +y -y +0 -0 +∞ -∞ NaN
    +x +z -z +∞ -∞ +0 -0 NaN
    -x -z +z -∞ +∞ -0 +0 NaN
    +0 +0 -0 NaN NaN +0 -0 NaN
    -0 -0 +0 NaN NaN -0 +0 NaN
    +∞ +∞ -∞ +∞ -∞ NaN NaN NaN
    -∞ -∞ +∞ -∞ +∞ NaN NaN NaN
    NaN NaN NaN NaN NaN NaN NaN NaN
  • Pembagian desimal:

    decimal operator /(decimal x, decimal y);
    

    Jika nilai operand kanan adalah nol, System.DivideByZeroException akan dilemparkan. Jika besarnya nilai yang dihasilkan terlalu besar untuk diwakili dalam format desimal, System.OverflowException dilemparkan. Karena pembulatan, hasilnya mungkin nol meskipun operand pertama bukan nol. Skala hasil, sebelum pembulatan apa pun, adalah skala terdekat dengan skala pilihan yang akan mempertahankan hasil yang sama dengan hasil yang tepat. Skala yang disukai adalah skala x lebih sedikit skala y.

    Pembagian desimal setara dengan menggunakan operator divisi jenis System.Decimal.

Bentuk terangkat (§12.4.8) dari operator pembagian yang telah ditentukan sebelumnya sebagaimana didefinisikan di atas juga telah ditentukan sebelumnya.

12.12.4 Operator sisa

Untuk pengoperasian formulir x % y, resolusi kelebihan beban operator biner (§12.4.5) diterapkan untuk memilih implementasi operator tertentu. Operand dikonversi ke jenis parameter operator yang dipilih, dan jenis hasilnya adalah jenis pengembalian operator.

Operator sisa yang sudah ditentukan tercantum di bawah ini. Semua operator menghitung sisa pembagian antara x dan y.

  • Sisa bilangan bulat:

    int operator %(int x, int y);
    uint operator %(uint x, uint y);
    long operator %(long x, long y);
    ulong operator %(ulong x, ulong y);
    

    Hasil x % y adalah nilai yang dihasilkan oleh x – (x / y) * y. Jika y nol, System.DivideByZeroException akan dilemparkan.

    Jika operand kiri adalah nilai int atau long terkecil dan operand kanan adalah –1, maka System.OverflowException terjadi jika dan hanya jika x / y akan menyebabkan pengecualian.

  • Sisa titik mengambang:

    float operator %(float x, float y);
    double operator %(double x, double y);
    

    Tabel berikut mencantumkan hasil semua kemungkinan kombinasi nilai terbatas nonzero, nol, tak terbatas, dan NaN. Dalam tabel, x dan y adalah nilai terbatas positif. z adalah hasil dari x % y dan dihitung sebagai x – n * y, di mana n adalah bilangan bulat terbesar yang mungkin kurang dari atau sama dengan x / y. Metode komputasi sisa ini dianalogikan dengan yang digunakan untuk operan bilangan bulat, tetapi berbeda dari definisi IEC 60559 (di mana n adalah bilangan bulat yang paling dekat dengan x / y).

    +y -y +0 -0 +∞ -∞ NaN
    +x +z +z NaN NaN +x +x NaN
    -x -z -z NaN NaN -x -x NaN
    +0 +0 +0 NaN NaN +0 +0 NaN
    -0 -0 -0 NaN NaN -0 -0 NaN
    +∞ NaN NaN NaN NaN NaN NaN NaN
    -∞ NaN NaN NaN NaN NaN NaN NaN
    NaN NaN NaN NaN NaN NaN NaN NaN
  • Sisa desimal:

    decimal operator %(decimal x, decimal y);
    

    Jika nilai operand kanan adalah nol, System.DivideByZeroException akan dilemparkan. Ditentukan oleh implementasi kapan System.ArithmeticException (atau subkelasnya) dilemparkan. Implementasi yang sesuai tidak akan memberikan pengecualian untuk x % y dalam hal apa pun di mana x / y tidak memberikan pengecualian. Skala hasil, sebelum pembulatan apa pun, adalah yang lebih besar dari skala dua operan, dan tanda hasilnya, jika bukan nol, sama dengan x.

    Sisa desimal setara dengan menggunakan operator sisa jenis System.Decimal.

    Catatan: Aturan ini memastikan bahwa untuk setiap tipe, hasilnya tidak pernah memiliki tanda yang berlawanan dengan operand kiri. catatan akhir

Bentuk 'lifted' (§12.4.8) dari operator sisa yang telah ditentukan sebelumnya sebagaimana diuraikan di atas juga telah ditentukan sebelumnya.

12.12.5 Operator tambahan

Untuk pengoperasian formulir x + y, resolusi kelebihan beban operator biner (§12.4.5) diterapkan untuk memilih implementasi operator tertentu. Operand dikonversi ke jenis parameter operator yang dipilih, dan jenis hasilnya adalah jenis pengembalian operator.

Operator penambahan yang telah ditentukan sebelumnya tercantum di bawah ini. Untuk jenis numerik dan enumerasi, operator penambahan yang telah ditentukan sebelumnya menghitung jumlah dua operan. Ketika satu atau kedua operand berjenis string, operator penambahan yang telah ditentukan menggabungkan representasi string dari operand.

  • Penambahan bilangan bulat:

    int operator +(int x, int y);
    uint operator +(uint x, uint y);
    long operator +(long x, long y);
    ulong operator +(ulong x, ulong y);
    

    Dalam konteks checked, jika jumlah berada di luar rentang jenis hasil, System.OverflowException akan dilemparkan. Dalam konteks unchecked, luapan tidak dilaporkan dan bit urutan tinggi yang signifikan yang berada di luar rentang tipe hasil diabaikan.

  • Penambahan titik mengambang:

    float operator +(float x, float y);
    double operator +(double x, double y);
    

    Jumlah dihitung sesuai dengan aturan aritmatika IEC 60559. Tabel berikut mencantumkan hasil semua kemungkinan kombinasi nilai terbatas nonzero, nol, tak terbatas, dan NaN. Dalam tabel, x dan y adalah nilai terbatas bukan nol, dan z adalah hasil dari x + y. Jika x dan y memiliki besaran yang sama tetapi tanda yang berlawanan, z adalah nol positif. Jika x + y terlalu besar untuk diwakili dalam jenis tujuan, z adalah tak terbatas dengan tanda yang sama dengan x + y.

    y +0 -0 +∞ -∞ NaN
    x z x x +∞ -∞ NaN
    +0 y +0 +0 +∞ –∞ NaN
    -0 y +0 -0 +∞ -∞ NaN
    +∞ +∞ +∞ +∞ +∞ NaN NaN
    -∞ -∞ -∞ -∞ NaN -∞ NaN
    NaN NaN NaN NaN NaN NaN NaN
  • Penambahan desimal:

    decimal operator +(decimal x, decimal y);
    

    Jika besarnya nilai yang dihasilkan terlalu besar untuk diwakili dalam format desimal, System.OverflowException dilemparkan. Skala hasil, sebelum pembulatan apa pun, merupakan skala yang lebih besar dari antara skala dua operan.

    Penambahan desimal setara dengan menggunakan operator penambahan jenis System.Decimal.

  • Penambahan enumerasi. Setiap jenis enumerasi secara implisit menyediakan operator yang telah ditentukan sebelumnya berikut, di mana E adalah jenis enum, dan U adalah jenis Eyang mendasarinya :

    E operator +(E x, U y);
    E operator +(U x, E y);
    

    Saat runtime, operator ini dievaluasi persis seperti (E)((U)x + (U)y).

  • Penggabungan string:

    string operator +(string x, string y);
    string operator +(string x, object y);
    string operator +(object x, string y);
    

    Fungsi kelebihan beban operator biner + ini melakukan penggabungan string. Jika operan perangkaian string adalah null, maka string kosong akan digunakan sebagai pengganti. Jika tidak, setiap operand non-string dikonversi ke representasi stringnya dengan memanggil metode ToString virtual yang diwarisi dari jenis object. Jika ToString mengembalikan null, string kosong diganti.

    Contoh:

    class Test
    {
        static void Main()
        {
            string s = null;
            Console.WriteLine("s = >" + s + "<");  // Displays s = ><
    
            int i = 1;
            Console.WriteLine("i = " + i);         // Displays i = 1
    
            float f = 1.2300E+15F;
            Console.WriteLine("f = " + f);         // Displays f = 1.23E+15
    
            decimal d = 2.900m;
            Console.WriteLine("d = " + d);         // Displays d = 2.900
       }
    }
    

    Output yang ditampilkan dalam komentar adalah hasil umum pada sistem US-English. Output yang tepat mungkin bergantung pada pengaturan regional lingkungan eksekusi. Operator penggabungan string itu sendiri berfungsi dengan cara yang sama dalam setiap kasus, tetapi metode ToString yang secara implisit dipanggil selama eksekusi mungkin dipengaruhi oleh setelan lokal.

    contoh akhir

    Hasil dari operator perangkaian string adalah string yang terdiri dari karakter operand kiri diikuti oleh karakter operand kanan. Operator penggabungan string tidak pernah mengembalikan nilai null. System.OutOfMemoryException dapat dilemparkan jika tidak ada cukup memori yang tersedia untuk mengalokasikan string yang dihasilkan.

  • Mendelegasikan kombinasi. Setiap jenis delegasi secara implisit menyediakan operator yang telah ditentukan sebelumnya berikut, di mana D adalah jenis delegasi:

    D operator +(D x, D y);
    

    Jika operand pertama null, hasil operasi adalah nilai operand kedua (bahkan jika itu juga null). Jika tidak, jika operand kedua adalah null, maka hasil operasi adalah nilai operand pertama. Jika tidak, hasil operasi adalah instans delegasi baru yang daftar pemanggilannya mencakup elemen-elemen dalam daftar pemanggilan dari operand pertama, diikuti dengan elemen-elemen dalam daftar pemanggilan dari operand kedua. Artinya, daftar pemanggilan delegasi yang dihasilkan adalah gabungan daftar pemanggilan dari kedua operan.

    Catatan: Untuk contoh kombinasi delegasi, lihat §12.12.6 dan §21.6. Karena bukan jenis delegasi, operator + tidak didefinisikan untuk itu. catatan akhir

Bentuk pengangkatan (§12.4.8) dari operator penambahan bawaan yang tidak diangkat, seperti yang didefinisikan di atas, juga telah ditentukan sebelumnya.

12.12.6 Operator pengurangan

Untuk pengoperasian formulir x – y, resolusi kelebihan beban operator biner (§12.4.5) diterapkan untuk memilih implementasi operator tertentu. Operand dikonversi ke jenis parameter operator yang dipilih, dan jenis hasilnya adalah jenis pengembalian operator.

Operator pengurangan yang telah ditentukan sebelumnya tercantum di bawah ini. Semua operator melakukan pengurangan y dari x.

  • Pengurangan bilangan bulat:

    int operator –(int x, int y);
    uint operator –(uint x, uint y);
    long operator –(long x, long y);
    ulong operator –(ulong x, ulong y
    

    Dalam konteks checked, jika perbedaannya berada di luar rentang jenis hasil, System.OverflowException akan dilemparkan. Dalam konteks unchecked, luapan tidak dilaporkan dan bit urutan tinggi yang signifikan yang berada di luar rentang tipe hasil diabaikan.

  • Pengurangan bilangan titik mengambang

    float operator –(float x, float y);
    double operator –(double x, double y);
    

    Perbedaannya dihitung sesuai dengan aturan aritmatika IEC 60559. Tabel berikut mencantumkan hasil semua kemungkinan kombinasi nilai terbatas nonzero, nol, tak terbatas, dan NaN. Dalam tabel, x dan y adalah nilai terbatas bukan nol, dan z adalah hasil dari x – y. Jika x dan y sama, z adalah nol positif. Jika x – y terlalu besar untuk diwakili dalam jenis tujuan, z adalah tak terbatas dengan tanda yang sama dengan x – y.

    y +0 -0 +∞ -∞ NaN
    x z x x -∞ +∞ NaN
    +0 -y +0 +0 -∞ +∞ NaN
    -0 -y -0 +0 -∞ +∞ NaN
    +∞ +∞ +∞ +∞ NaN +∞ NaN
    -∞ -∞ -∞ -∞ -∞ NaN NaN
    NaN NaN NaN NaN NaN NaN NaN

    (Dalam tabel di atas, entri menunjukkan negasi , bukan berarti nilainya negatif.)

  • Pengurangan desimal:

    decimal operator –(decimal x, decimal y);
    

    Jika besarnya nilai yang dihasilkan terlalu besar untuk diwakili dalam format desimal, System.OverflowException dilemparkan. Skala hasil, sebelum pembulatan apa pun, merupakan skala yang lebih besar dari antara skala dua operan.

    Pengurangan desimal setara dengan menggunakan operator pengurangan jenis System.Decimal.

  • Pengurangan enumerasi. Setiap jenis enumerasi secara implisit menyediakan operator yang telah ditentukan sebelumnya berikut, di mana E adalah jenis enum, dan U adalah jenis Eyang mendasarinya:

    U operator –(E x, E y);
    

    Operator ini dievaluasi persis seperti (U)((U)x – (U)y). Dengan kata lain, operator menghitung perbedaan antara nilai ordinal x dan y, dan jenis hasilnya adalah jenis enumerasi yang mendasar.

    E operator –(E x, U y);
    

    Operator ini dievaluasi persis seperti (E)((U)x – y). Dengan kata lain, operator mengurangi nilai dari tipe dasar dari enumerasi, menghasilkan nilai dari enumerasi.

  • Penghapusan delegasi. Setiap jenis delegasi secara implisit menyediakan operator yang telah ditentukan sebelumnya berikut, di mana D adalah jenis delegasi:

    D operator –(D x, D y);
    

    Semantiknya adalah sebagai berikut:

    • Jika operand pertama adalah null, maka hasil operasinya adalah null.
    • Jika tidak, jika operand kedua adalah null, maka hasil operasi adalah nilai operand pertama.
    • Jika tidak, kedua operan mewakili daftar pemanggilan yang tidak kosong (§21,2).
      • Jika daftar dibandingkan sama, seperti yang ditentukan oleh operator kesetaraan delegasi (§12.14.9), hasil operasinya adalah null.
      • Selain itu, hasil operasi adalah daftar panggilan baru yang terdiri dari daftar operand pertama yang entri operand keduanya telah dihapus, asalkan daftar operand kedua adalah sublist dari operand pertama. (Untuk menentukan kesetaraan sublist, entri-entri yang bersesuaian dibandingkan seperti dalam operator kesetaraan delegasi.) Jika daftar operand kedua cocok dengan beberapa sublist yang terdiri dari entri berdekatan dalam daftar operand pertama, sublist berdekatan terakhir yang sesuai akan dihapus.
      • Jika tidak, hasil operasi adalah nilai operand kiri.

    Tidak satu pun dari daftar-daftar operand (jika ada) yang mengalami perubahan dalam proses.

    Contoh:

    delegate void D(int x);
    
    class C
    {
        public static void M1(int i) { ... }
        public static void M2(int i) { ... }
    }
    
    class Test
    {
        static void Main()
        {
            D cd1 = new D(C.M1);
            D cd2 = new D(C.M2);
            D list = null;
    
            list = null - cd1;                             // null
            list = (cd1 + cd2 + cd2 + cd1) - null;         // M1 + M2 + M2 + M1
            list = (cd1 + cd2 + cd2 + cd1) - cd1;          // M1 + M2 + M2
            list = (cd1 + cd2 + cd2 + cd1) - (cd1 + cd2);  // M2 + M1
            list = (cd1 + cd2 + cd2 + cd1) - (cd2 + cd2);  // M1 + M1
            list = (cd1 + cd2 + cd2 + cd1) - (cd2 + cd1);  // M1 + M2
            list = (cd1 + cd2 + cd2 + cd1) - (cd1 + cd1);  // M1 + M2 + M2 + M1
            list = (cd1 + cd2 + cd2 + cd1) - (cd1 + cd2 + cd2 + cd1);  // null
        }
    }
    

    contoh akhir

Bentuk diangkat (§12.4.8) dari operator pengurangan yang sudah ditentukan di atas juga telah ditentukan sebelumnya.

12.13 Operator shift

Operator << dan >> digunakan untuk melakukan operasi pergeseran bit.

shift_expression
    : additive_expression
    | shift_expression '<<' additive_expression
    | shift_expression right_shift additive_expression
    ;

Jika operan shift_expression memiliki jenis waktu kompilasi dynamic, maka ekspresi terikat secara dinamis (§12.3.3). Dalam hal ini, tipe kompilasi-waktu ekspresi adalah dynamic, dan resolusi yang dijelaskan di bawah ini akan berlangsung pada run-time menggunakan tipe run-time dari operand yang memiliki tipe kompilasi-waktu dynamic.

Untuk pengoperasian formulir x << count atau x >> count, resolusi kelebihan beban operator biner (§12,4,5) diterapkan untuk memilih implementasi operator tertentu. Operand dikonversi ke jenis parameter operator yang dipilih, dan jenis hasilnya adalah jenis pengembalian operator.

Saat mendeklarasikan operator shift yang kelebihan beban, jenis operand pertama harus selalu menjadi kelas atau struct yang berisi deklarasi operator, dan jenis operand kedua akan selalu int.

Operator shift yang telah ditentukan sebelumnya tercantum di bawah ini.

  • Geser ke kiri:

    int operator <<(int x, int count);
    uint operator <<(uint x, int count);
    long operator <<(long x, int count);
    ulong operator <<(ulong x, int count);
    

    Operator << menggeser x ke kiri oleh sejumlah bit yang dihitung seperti dijelaskan di bawah ini.

    Bit berurutan tinggi di luar rentang jenis hasil x dibuang, bit yang tersisa digeser ke kiri, dan posisi bit kosong berurutan rendah diatur ke nol.

  • Geser ke kanan:

    int operator >>(int x, int count);
    uint operator >>(uint x, int count);
    long operator >>(long x, int count);
    ulong operator >>(ulong x, int count);
    

    Operator >> menggeser x ke kanan sejumlah bit yang dihitung seperti yang dijelaskan di bawah ini.

    Ketika x berjenis int atau long, bit urutan rendah x dibuang, bit yang tersisa digeser ke kanan, dan posisi bit kosong urutan tinggi diatur ke nol jika x tidak negatif dan diatur ke satu jika x negatif.

    Ketika x berjenis uint atau ulong, bit urutan rendah x dibuang, bit yang tersisa digeser ke kanan, dan posisi bit kosong urutan tinggi diatur ke nol.

Untuk operator yang telah ditentukan sebelumnya, jumlah bit yang akan digeser dihitung sebagai berikut:

  • Ketika jenis x adalah int atau uint, jumlah pergeseran ditentukan oleh lima bit urutan rendah dari count. Dengan kata lain, jumlah shift dihitung dari count & 0x1F.
  • Ketika jenis x adalah long atau ulong, jumlah shift diberikan oleh enam bit paling rendah dari count. Dengan kata lain, jumlah shift dihitung dari count & 0x3F.

Jika jumlah shift yang dihasilkan adalah nol, operator shift hanya mengembalikan nilai x.

Operasi shift tidak pernah menyebabkan luapan dan menghasilkan hasil yang sama dalam konteks yang diperiksa dan tidak dicentang.

Ketika operand kiri operator >> adalah tipe integral bertanda, operator melakukan pergeseran aritmatika ke kanan di mana nilai bit paling signifikan (bit tanda) dari operan ditularkan ke posisi bit kosong urutan tinggi. Ketika operand kiri operator >> adalah tipe integral tidak bertanda, operator melakukan pergeseran ke kanan logis di mana posisi bit kosong paling signifikan selalu diatur ke nol. Untuk melakukan operasi yang kebalikan dari yang ditentukan oleh jenis operand, cast eksplisit dapat digunakan.

Contoh: Jika x adalah variabel jenis int, operasi unchecked ((int)((uint)x >> y)) melakukan pergeseran logis ke kanan x. contoh akhir

Bentuk angkat (§12.4.8) dari operator shift yang tidak diangkat yang telah ditentukan di atas juga telah ditentukan sebelumnya.

12.14 Operator relasional dan pengujian jenis

12.14.1 Umum

Operator ==, !=, <, >, <=, >=, is, dan as disebut operator relasional dan pengujian jenis.

relational_expression
    : shift_expression
    | relational_expression '<' shift_expression
    | relational_expression '>' shift_expression
    | relational_expression '<=' shift_expression
    | relational_expression '>=' shift_expression
    | relational_expression 'is' type
    | relational_expression 'is' pattern
    | relational_expression 'as' type
    ;

equality_expression
    : relational_expression
    | equality_expression '==' relational_expression
    | equality_expression '!=' relational_expression
    ;

Catatan: Pencarian untuk operan kanan dari operator is harus diuji terlebih dahulu sebagai tipe , kemudian sebagai ekspresi yang dapat mencakup beberapa token. Dalam kasus di mana operand adalah ekspresi , ekspresi pola harus memiliki prioritas setidaknya setinggi shift_expression. catatan akhir

Catatan: Ada ambiguitas tata bahasa antara jenis dan constant_pattern di relational_expression sisi iskanan ; mungkin merupakan penguraian yang valid dari pengidentifikasi yang memenuhi syarat. Dalam kasus seperti itu, hanya jika gagal mengikat sebagai jenis (untuk kompatibilitas dengan versi bahasa sebelumnya), apakah itu diselesaikan menjadi hal pertama yang ditemukan (yang harus berupa konstanta atau jenis). Ambiguitas ini hanya ada di sisi kanan ekspresi seperti itu.

Operator is dijelaskan dalam §12.14.12 dan as operator dijelaskan dalam §12.14.13.

Operator ==, !=, <, >, <=, dan >= adalah operator perbandingan .

Jika default_literal (§12.8.21) digunakan sebagai operan operator <, >, <=, atau >=, kesalahan waktu kompilasi terjadi. Jika default_literal digunakan sebagai kedua operan dari operator == atau !=, maka kesalahan waktu kompilasi terjadi. Jika default_literal digunakan sebagai operand kiri operator is atau as, kesalahan waktu kompilasi terjadi.

Jika operan operator perbandingan memiliki jenis waktu kompilasi dynamic, maka ekspresi terikat secara dinamis (§12.3.3). Dalam kasus ini, jenis waktu kompilasi dari ekspresi tersebut adalah dynamic, dan resolusi yang dijelaskan di bawah ini akan terjadi saat runtime menggunakan jenis runtime dari operand yang memiliki jenis waktu kompilasi dynamic.

Untuk pengoperasian formulir x «op» y, di mana «op» adalah operator perbandingan, resolusi kelebihan beban (§12.4.5) diterapkan untuk memilih implementasi operator tertentu. Operand dikonversi ke jenis parameter operator yang dipilih, dan jenis hasilnya adalah jenis pengembalian operator. Jika kedua operan equality_expression adalah literal null, maka resolusi kelebihan beban tidak dilakukan dan ekspresi akan bernilai konstan true atau false tergantung pada operator == atau !=.

Operator perbandingan yang telah ditentukan sebelumnya dijelaskan dalam subklaus berikut. Semua operator perbandingan yang telah ditentukan sebelumnya mengembalikan hasil jenis bool, seperti yang dijelaskan dalam tabel berikut.

Operasi Hasil
x == y true jika x sama dengan y, false sebaliknya
x != y true jika x tidak sama dengan y, false sebaliknya
x < y true jika x kurang dari y, false sebaliknya
x > y true jika x lebih besar dari y, false sebaliknya
x <= y true jika x kurang dari atau sama dengan y, false sebaliknya
x >= y true jika x lebih besar dari atau sama dengan y, false sebaliknya

12.14.2 Operator perbandingan bilangan bulat

Operator perbandingan bilangan bulat yang telah ditentukan sebelumnya adalah:

bool operator ==(int x, int y);
bool operator ==(uint x, uint y);
bool operator ==(long x, long y);
bool operator ==(ulong x, ulong y);

bool operator !=(int x, int y);
bool operator !=(uint x, uint y);
bool operator !=(long x, long y);
bool operator !=(ulong x, ulong y);

bool operator <(int x, int y);
bool operator <(uint x, uint y);
bool operator <(long x, long y);
bool operator <(ulong x, ulong y);

bool operator >(int x, int y);
bool operator >(uint x, uint y);
bool operator >(long x, long y);
bool operator >(ulong x, ulong y);

bool operator <=(int x, int y);
bool operator <=(uint x, uint y);
bool operator <=(long x, long y);
bool operator <=(ulong x, ulong y);

bool operator >=(int x, int y);
bool operator >=(uint x, uint y);
bool operator >=(long x, long y);
bool operator >=(ulong x, ulong y);

Masing-masing operator ini membandingkan nilai numerik dari dua operan bilangan bulat dan mengembalikan nilai bool yang menunjukkan apakah hubungan tertentu true atau false.

Bentuk angkat (§12.4.8) dari operator perbandingan bilangan bulat yang tidak diangkat telah ditentukan sebelumnya juga telah didefinisikan sebelumnya.

12.14.3 Operator perbandingan floating-point

Operator perbandingan floating-point yang telah ditentukan sebelumnya adalah sebagai berikut:

bool operator ==(float x, float y);
bool operator ==(double x, double y);

bool operator !=(float x, float y);
bool operator !=(double x, double y);

bool operator <(float x, float y);
bool operator <(double x, double y);

bool operator >(float x, float y);
bool operator >(double x, double y);

bool operator <=(float x, float y);
bool operator <=(double x, double y);

bool operator >=(float x, float y);
bool operator >=(double x, double y);

Operator membandingkan operand sesuai dengan aturan standar IEC 60559:

Jika salah satu operand adalah NaN, hasilnya false untuk semua operator kecuali !=, yang hasilnya true. Untuk dua operan apa pun, x != y selalu menghasilkan hasil yang sama seperti !(x == y). Namun, ketika satu atau kedua operan adalah NaN, operator <, >, <=, dan >= tidak menghasilkan hasil yang sama dengan negasi logis operator yang berlawanan.

Contoh: Jika salah satu x dan y adalah NaN, maka x < yfalse, tetapi !(x >= y)true. contoh akhir

Ketika tidak ada operan yang NaN, operator membandingkan nilai dari dua operan floating-point berdasarkan urutan.

–∞ < –max < ... < –min < –0.0 == +0.0 < +min < ... < +max < +∞

di mana min dan max adalah nilai terbatas positif terkecil dan terbesar yang dapat diwakili dalam format floating-point yang diberikan. Efek penting dari pemesanan ini adalah:

  • Nol negatif dan positif dianggap sama.
  • Tak terbatas negatif dianggap kurang dari semua nilai lainnya, tetapi sama dengan tak terbatas negatif lainnya.
  • Infinitas positif dianggap lebih besar dari semua nilai lainnya, tetapi sama dengan infinitas positif yang lain.

Bentuk terangkat (§12.4.8) dari operator perbandingan floating-point yang didefinisikan di atas juga telah ditentukan sebelumnya.

12.14.4 Operator perbandingan desimal

Operator perbandingan desimal yang telah ditentukan sebelumnya adalah:

bool operator ==(decimal x, decimal y);
bool operator !=(decimal x, decimal y);
bool operator <(decimal x, decimal y);
bool operator >(decimal x, decimal y);
bool operator <=(decimal x, decimal y);
bool operator >=(decimal x, decimal y);

Masing-masing operator ini membandingkan nilai numerik dari dua operand desimal dan mengembalikan nilai bool yang menunjukkan apakah hubungan tertentu true atau false. Setiap perbandingan desimal setara dengan menggunakan operator relasional atau kesetaraan yang sesuai dari jenis System.Decimal.

Bentuk pengangkatan (§12.4.8) dari operator perbandingan desimal yang tidak diangkat yang telah ditentukan sebelumnya juga telah ditentukan.

12.14.5 Operator kesetaraan Boolean

Operator kesetaraan Boolean yang telah ditentukan sebelumnya adalah:

bool operator ==(bool x, bool y);
bool operator !=(bool x, bool y);

Hasil dari == adalah true jika baik x dan y adalah true, atau jika baik x dan y adalah false. Jika tidak, hasilnya false.

Hasil dari != adalah false jika baik x dan y adalah true, atau jika baik x dan y adalah false. Jika tidak, hasilnya true. Ketika operand berjenis bool, operator != menghasilkan hasil yang sama dengan operator ^.

Bentuk yang diangkat (§12.4.8) dari operator kesetaraan Boolean prapaket yang tidak diangkat yang ditentukan di atas juga telah ditentukan sebelumnya.

12.14.6 Operator perbandingan enumerasi

Setiap jenis enumerasi secara implisit menyediakan operator perbandingan yang telah ditentukan sebelumnya berikut

bool operator ==(E x, E y);
bool operator !=(E x, E y);

bool operator <(E x, E y);
bool operator >(E x, E y);
bool operator <=(E x, E y);
bool operator >=(E x, E y);

Hasil evaluasi x «op» y, di mana x dan y adalah ekspresi dari jenis enumerasi E dengan jenis yang mendasari U, dan «op» adalah salah satu operator perbandingan, sama persis dengan mengevaluasi ((U)x) «op» ((U)y). Dengan kata lain, operator perbandingan jenis enumerasi hanya membandingkan nilai integral yang mendasari dua operan.

Bentuk terangkat (§12.4.8) dari operator perbandingan enumerasi bawaan yang telah ditentukan di atas juga telah ditentukan sebelumnya.

12.14.7 Operator kesetaraan jenis referensi

Setiap jenis kelas C secara implisit menyediakan operator kesetaraan jenis referensi yang telah ditentukan berikut:

bool operator ==(C x, C y);
bool operator !=(C x, C y);

kecuali jika operator kesetaraan yang sudah ditentukan itu ada untuk C (misalnya, ketika C adalah string atau System.Delegate).

Operator mengembalikan hasil membandingkan dua referensi untuk kesetaraan atau non-kesetaraan. operator == mengembalikan true jika dan hanya jika x dan y merujuk ke instans yang sama atau keduanya null, sementara operator != mengembalikan true jika dan hanya jika operator == dengan operan yang sama akan mengembalikan false.

Selain aturan penerapan normal (§12.6.4.2), operator kesetaraan jenis referensi yang telah ditentukan sebelumnya memerlukan salah satu hal berikut agar berlaku:

  • Kedua operan adalah nilai dari jenis yang dikenal sebagai reference_type atau nullharfiah . Selain itu, terdapat konversi identitas atau referensi eksplisit (§10.3.5) dari salah satu operan ke jenis operan lainnya.
  • Satu operan adalah nullharfiah , dan operand lainnya adalah nilai jenis T di mana T adalah type_parameter yang tidak diketahui sebagai jenis nilai, dan tidak memiliki batasan jenis nilai.
    • Jika pada waktu runtime T adalah jenis nilai yang tidak dapat bernilai null, hasil dari == adalah false, dan hasil dari != adalah true.
    • Jika pada runtime T adalah jenis nilai nullable, hasilnya dihitung dari HasValue properti operand, seperti yang dijelaskan dalam (§12.14.10).
    • Jika saat runtime T adalah tipe referensi, hasilnya adalah true jika operand adalah null, dan false sebaliknya.

Kecuali salah satu kondisi ini benar, kesalahan pada waktu pengikatan terjadi.

Catatan: Implikasi penting dari aturan ini adalah:

  • Merupakan kesalahan pada waktu pengikatan jika menggunakan operator kesetaraan jenis referensi bawaan untuk membandingkan dua referensi yang sudah diketahui berbeda pada saat itu. Misalnya, jika jenis operand berdasarkan waktu pengikatan adalah dua jenis kelas, dan jika tidak ada yang berasal dari yang lain, maka tidak mungkin bagi kedua operand untuk merujuk pada objek yang sama. Dengan demikian, operasi dianggap sebagai kesalahan saat pengikatan.
  • Operator kesetaraan dari jenis referensi yang sudah ditentukan sebelumnya tidak mengizinkan operand jenis nilai untuk dibandingkan (kecuali ketika parameter tipe dibandingkan dengan null, yang ditangani secara khusus).
  • Operan operator kesetaraan jenis referensi yang telah ditentukan sebelumnya tidak pernah dikotak. Tidak ada artinya untuk melakukan operasi pembungkusan seperti itu, karena referensi ke instans terbungkus yang baru dialokasikan pasti berbeda dengan semua referensi lainnya.

Untuk operasi dengan bentuk x == y atau x != y, jika terdapat operator == atau operator != yang didefinisikan oleh pengguna dan berlaku, aturan resolusi overload operator (§12.4.5) akan memilih operator tersebut daripada operator kesetaraan tipe referensi yang telah ditentukan sebelumnya. Selalu dimungkinkan untuk memilih operator kesetaraan jenis referensi yang sudah ditentukan dengan secara eksplisit mengonversi salah satu atau kedua operand ke tipe object.

catatan akhir

Contoh: Contoh berikut memeriksa apakah argumen dari parameter tipe yang tidak dibatasi adalah null.

class C<T>
{
   void F(T x)
   {
      if (x == null)
      {
          throw new ArgumentNullException();
      }
      ...
   }
}

Konstruksi x == null diizinkan meskipun T dapat mewakili jenis nilai yang tidak dapat diubah ke null, dan hasilnya hanya didefinisikan false ketika T adalah jenis nilai yang tidak dapat diubah ke null.

contoh akhir

Untuk operasi dalam bentuk x == y atau x != y, jika ada operator == atau operator != yang berlaku, aturan resolusi kelebihan beban operator (aturan§12.4.5) akan memilih operator tersebut, alih-alih operator kesetaraan tipe referensi yang sudah ditetapkan sebelumnya.

Catatan: Selalu dimungkinkan untuk memilih operator kesetaraan tipe referensi yang telah ditentukan sebelumnya dengan melakukan casting kedua operan secara eksplisit ke tipe object. catatan akhir

Contoh: Contoh

class Test
{
    static void Main()
    {
        string s = "Test";
        string t = string.Copy(s);
        Console.WriteLine(s == t);
        Console.WriteLine((object)s == t);
        Console.WriteLine(s == (object)t);
        Console.WriteLine((object)s == (object)t);
    }
}

menghasilkan output

True
False
False
False

Variabel s dan t mengacu pada dua instans string yang berbeda yang berisi karakter yang sama. Output perbandingan True pertama karena operator kesetaraan string yang telah ditentukan sebelumnya (§12.14.8) dipilih ketika kedua operan berjenis string. Perbandingan yang tersisa semuanya mengeluarkan False karena overload operator == dalam tipe string tidak berlaku ketika salah satu operand memiliki tipe waktu pengikatan object.

Perhatikan bahwa teknik di atas tidak bermakna untuk jenis nilai. Contoh

class Test
{
    static void Main()
    {
        int i = 123;
        int j = 123;
        Console.WriteLine((object)i == (object)j);
    }
}

Menghasilkan False karena pemaksaan menciptakan referensi ke dua instans terpisah dari nilai int yang terkotakkan.

contoh akhir

12.14.8 Operator kesetaraan string

Operator kesetaraan string yang telah ditentukan sebelumnya adalah:

bool operator ==(string x, string y);
bool operator !=(string x, string y);

Dua nilai string dianggap sama ketika salah satu dari berikut ini benar:

  • Kedua nilai tersebut adalah null.
  • Kedua nilai tersebut adalah referensi non-null ke instans string yang memiliki panjang identik dan karakter identik di setiap posisi karakter.

Operator perbandingan kesetaraan string membandingkan nilai string alih-alih referensi string. Ketika dua instans string terpisah berisi urutan karakter yang sama persis, nilai string sama, tetapi referensinya berbeda.

Catatan: Seperti yang dijelaskan dalam §12.14.7, operator kesetaraan jenis referensi dapat digunakan untuk membandingkan referensi string alih-alih nilai string. catatan akhir

12.14.9 Mendelegasikan operator kesetaraan

Operator kesetaraan delegasi yang telah ditentukan sebelumnya adalah:

bool operator ==(System.Delegate x, System.Delegate y);
bool operator !=(System.Delegate x, System.Delegate y);

Dua instans delegasi dianggap sama menurut kriteria berikut:

  • Jika salah satu dari instans delegasi adalah null, maka mereka sama jika dan hanya jika keduanya adalah null.
  • Jika delegasi memiliki jenis waktu berjalan yang berbeda, mereka tidak pernah sama.
  • Jika kedua instans delegasi memiliki daftar pemanggilan (§21.2), instans tersebut sama jika dan hanya jika daftar pemanggilannya memiliki panjang yang sama, dan setiap entri dalam daftar pemanggilan seseorang sama (seperti yang didefinisikan di bawah) dengan entri yang sesuai, secara berurutan, dalam daftar pemanggilan lainnya.

Aturan berikut mengatur kesetaraan entri daftar pemanggilan:

  • Jika dua entri daftar pemanggilan keduanya merujuk ke metode statis yang sama maka entri sama.
  • Jika dua entri daftar pemanggilan keduanya merujuk ke metode non-statis yang sama pada objek target yang sama (seperti yang didefinisikan oleh operator kesetaraan referensi) maka entri sama.
  • Entri daftar pemanggilan yang dihasilkan dari evaluasi fungsi anonim yang identik secara semantik (§12,21) dengan set yang sama (mungkin kosong) dari instans variabel luar yang ditangkap diizinkan (tetapi tidak diperlukan) agar sama.

Jika resolusi kelebihan beban operator diselesaikan untuk mendelegasikan operator kesetaraan, dan jenis waktu pengikatan dari kedua operand adalah jenis delegasi seperti yang dijelaskan dalam §21 daripada System.Delegate, dan tidak ada konversi identitas antara jenis operand jenis pengikatan, kesalahan waktu pengikatan terjadi.

Catatan: Aturan ini mencegah perbandingan yang tidak pernah dapat menganggap nilai non-null sebagai setara karena merujuk pada instans dari jenis delegasi yang berbeda. catatan akhir

12.14.10 Operator kesetaraan antara jenis nilai nullable dan literal null

Operator == dan != mengizinkan satu operand menjadi nilai dari tipe nilai yang dapat bernilai null dan yang lain menjadi literal null, bahkan jika tidak ada operator yang telah ditentukan atau ditentukan pengguna (dalam bentuk non-angkat atau terangkat) untuk operasi tersebut.

Untuk operasi salah satu formulir

x == null    null == x    x != null    null != x

di mana x adalah ekspresi dari jenis nilai nullable, jika resolusi kelebihan beban operator (§12.4.5) gagal menemukan operator yang berlaku, hasilnya dihitung dari properti HasValue dari x. Secara khusus, dua bentuk pertama diterjemahkan ke dalam !x.HasValue, dan dua bentuk terakhir diterjemahkan ke dalam x.HasValue.

12.14.11 Operator kesetaraan Tuple

Operator kesetaraan tuple diterapkan satu per satu pada elemen dari operan tuple dalam urutan leksikal.

Jika setiap operan x dan y dari operator == atau != diklasifikasikan sebagai tuple atau sebagai nilai dengan tipe tuple (§8.3.11), operator adalah operator kesetaraan tuple .

Jika suatu operan e diklasifikasikan sebagai tuple, elemen e1...en akan menjadi hasil evaluasi dari ekspresi elemen dalam ekspresi tuple. Jika tidak, jika e adalah nilai dari jenis tuple, elemen yang harus dihasilkan adalah t.Item1...t.Itemn, di mana t merupakan hasil evaluasi dari e.

Operan x dan y pada operator kesetaraan tuple harus memiliki aritas yang sama, atau terjadi kesalahan waktu kompilasi. Untuk setiap pasangan elemen xi dan yi, operator kesetaraan yang sama akan berlaku, dan akan menghasilkan hasil dari jenis bool, dynamic, jenis yang memiliki konversi implisit ke bool, atau jenis yang mendefinisikan operator true dan false.

Operator kesamaan tuple x == y dievaluasi sebagai berikut:

  • Operand di sisi kiri x dievaluasi.
  • Operan di sisi kanan y dievaluasi.
  • Untuk setiap pasangan elemen xi dan yi dalam urutan leksikal:
    • Operator xi == yi dievaluasi, dan hasil dari jenis bool diperoleh dengan cara berikut:
      • Jika perbandingan menghasilkan bool maka itulah hasilnya.
      • Jika tidak, jika perbandingan menghasilkan dynamic maka operator false dipanggil secara dinamis di atasnya, dan nilai bool yang dihasilkan dinegasikan dengan operator negasi logis (!).
      • Jika tidak, jenis perbandingan yang memiliki konversi implisit ke boolakan menerapkan konversi tersebut.
      • Jika tidak, jika jenis perbandingan memiliki operator false, operator tersebut dipanggil dan nilai bool yang dihasilkan dinegasikan dengan operator negasi logis (!).
    • Jika bool bernilai false, maka tidak ada evaluasi lebih lanjut yang terjadi, dan hasil operator kesetaraan susunan adalah false.
  • Jika setiap perbandingan elemen menghasilkan true, maka hasil operator kesetaraan tuple adalah true.

Operator kesamaan tuple x != y dievaluasi sebagai berikut:

  • Operand di sisi kiri x dievaluasi.
  • Operan di sisi kanan y dievaluasi.
  • Untuk setiap pasangan elemen xi dan yi dalam urutan leksikal:
    • Operator xi != yi dievaluasi, dan hasil dari jenis bool diperoleh dengan cara berikut:
      • Jika perbandingan menghasilkan bool maka itulah hasilnya.
      • Jika tidak, jika perbandingan menghasilkan dynamic maka operator true dipanggil secara dinamis di atasnya, dan nilai bool yang dihasilkan adalah hasilnya.
      • Jika tidak, jenis perbandingan yang memiliki konversi implisit ke boolakan menerapkan konversi tersebut.
      • Jika tidak, jika jenis perbandingan memiliki operator true, operator tersebut dipanggil dan nilai bool yang dihasilkan adalah hasilnya.
    • Jika bool bernilai true, maka tidak ada evaluasi lebih lanjut yang terjadi, dan hasil operator kesetaraan susunan adalah true.
  • Jika setiap perbandingan elemen menghasilkan false, maka hasil operator kesetaraan tuple adalah false.

12.14.12 Adalah operator

Ada dua bentuk operator is. Salah satunya adalah operator tipe-is , yang memiliki tipe di sisi kanan. Yang lain adalah operator is-pattern, yang memiliki pola di sisi kanan.

12.14.12.1 Operator is-type

Operator adalah operator tipe yang digunakan untuk memeriksa apakah jenis waktu proses suatu objek kompatibel dengan jenis tertentu. Pemeriksaan dilakukan pada runtime. Hasil operasi E is T, di mana E adalah ekspresi dan T adalah jenis selain dynamic, adalah nilai Boolean yang menunjukkan apakah E non-null dan dapat berhasil dikonversi ke jenis T oleh konversi referensi, konversi pembungkusan, konversi pelepasan, atau konversi pembongkaran.

Operasi E is T dievaluasi sebagai berikut:

  1. Jika E adalah fungsi anonim atau grup metode, kesalahan waktu kompilasi terjadi.
  2. Jika T adalah jenis referensi nullable (§8.9.3), kesalahan waktu kompilasi terjadi.
  3. Jika E adalah null harfiah, atau jika nilai Enull, hasilnya adalah false.
  4. Sebaliknya:
    1. Anggaplah R adalah jenis runtime dari E.
    2. Mari kita D berasal dari R sebagai berikut:
      1. Jika R adalah jenis nilai yang dapat diubah ke null, D adalah jenis Ryang mendasar.
      2. Jika tidak, D adalah R.
    3. Hasilnya tergantung pada D dan T sebagai berikut:
      1. Jika T adalah jenis referensi, hasilnya adalah true jika:
        • konversi identitas ada antara D dan T, atau
        • D adalah jenis referensi dan konversi referensi implisit dari D ke T ada, atau
        • D adalah jenis nilai dan konversi tinju dari D ke T yang ada.
      2. Jika T adalah tipe data yang bisa bernilai null, hasilnya adalah true jika D adalah jenis dasar dari T.
      3. Jika T adalah jenis nilai yang tidak dapat diubah ke null, hasilnya true jika D dan T adalah jenis yang sama.
      4. Jika tidak, hasilnya false.

Konversi yang ditentukan pengguna tidak dipertimbangkan oleh operator is.

Catatan: Karena operator is dievaluasi pada runtime, semua argumen jenis telah diganti dan tidak ada jenis terbuka (§8.4.3) untuk dipertimbangkan. catatan akhir

Catatan: Operator is dapat dipahami dalam hal jenis dan konversi waktu kompilasi sebagai berikut, di mana C adalah jenis waktu kompilasi E:

  • Jika jenis waktu kompilasi e sama dengan T, atau jika terdapat konversi referensi implisit (§10.2.8), konversi boxing (§10.2.9), konversi pembungkusan (§10.6), atau konversi pembongkaran eksplisit (§10.6) dari jenis waktu kompilasi E ke T:
    • Jika C adalah jenis nilai yang tidak dapat diubah ke null, hasil operasi true.
    • Jika tidak, hasil operasi setara dengan mengevaluasi E != null.
  • Jika tidak, jika konversi referensi eksplisit (§10.3.5) atau konversi unboxing (§10.3.7) ada dari C ke T, atau jika C atau T merupakan jenis terbuka (§8.4.3), maka pemeriksaan runtime seperti di atas akan dilakukan.
  • Jika tidak, referensi, pembungkusan, atau konversi dari E ke tipe T tidak mungkin dilakukan, dan hasil dari operasi tersebut adalah false. Pengkompilasi dapat menerapkan pengoptimalan berdasarkan jenis waktu kompilasi.

catatan akhir

12.14.12.2 Operator is-pattern

operator is-pattern digunakan untuk memeriksa apakah nilai yang dihitung oleh ekspresi sesuai dengan pola tertentu (§11). Pemeriksaan dilakukan pada runtime. Hasil operator is-pattern adalah true jika nilai cocok dengan pola; jika tidak, itu salah.

Untuk ekspresi bertipe E is P, di mana E adalah ekspresi relasional tipe T dan P adalah pola, ini adalah kesalahan pada waktu kompilasi jika salah satu kondisi berikut berlaku.

  • E tidak menunjuk nilai atau tidak memiliki tipe.
  • Pola P tidak berlaku (§11,2) ke jenis T.

Setiap single_variable_designation pola memperkenalkan variabel lokal baru yang pasti ditetapkan (§9,4) ketika relational_expression yang sesuai menguji true.

12.14.13 Operator sebagai

Operator as digunakan untuk mengonversi nilai secara eksplisit ke jenis referensi tertentu atau jenis nilai nullable. Tidak seperti ekspresi cast (§12.9.8), as operator tidak pernah melemparkan pengecualian. Sebaliknya, jika konversi yang ditunjukkan tidak dimungkinkan, nilai yang dihasilkan null.

Dalam operasi formulir E as T, E harus berupa ekspresi dan T harus merupakan jenis referensi, parameter jenis yang dikenal sebagai jenis referensi, atau jenis nilai yang dapat diubah ke null. Selain itu, setidaknya salah satu hal berikut ini akan benar, atau jika tidak, kesalahan waktu kompilasi terjadi:

Jika jenis waktu kompilasi E tidak dynamic, operasi E as T menghasilkan hasil yang sama seperti

E is T ? (T)(E) : (T)null

kecuali bahwa E hanya dievaluasi sekali. Kompilator dapat diharapkan untuk mengoptimalkan E as T untuk melakukan paling banyak satu pemeriksaan jenis runtime dibandingkan dengan dua pemeriksaan jenis runtime yang tersirat oleh ekspansi di atas.

Jika jenis waktu kompilasi E adalah dynamic, maka tidak seperti operator cast, operator as tidak terikat secara dinamis (§12.3.3). Oleh karena itu perluasan dalam hal ini adalah:

E is T ? (T)(object)(E) : (T)null

Perhatikan bahwa beberapa konversi, seperti konversi yang didefinisikan oleh pengguna, tidak dimungkinkan dengan operator as dan sebaliknya harus dilakukan menggunakan ekspresi cast.

Contoh: Dalam contoh

class X
{
    public string F(object o)
    {
        return o as string;  // OK, string is a reference type
    }

    public T G<T>(object o)
        where T : Attribute
    {
        return o as T;       // Ok, T has a class constraint
    }

    public U H<U>(object o)
    {
        return o as U;       // Error, U is unconstrained
    }
}

parameter jenis TG diketahui sebagai jenis referensi, karena memiliki batasan kelas. Jenis parameter U dari H tidak sesuai; oleh karena itu, penggunaan operator as di H tidak diizinkan.

contoh akhir

12.15 Operator logis

12.15.1 Umum

Operator &, ^, dan | disebut operator logis.

and_expression
    : equality_expression
    | and_expression '&' equality_expression
    ;

exclusive_or_expression
    : and_expression
    | exclusive_or_expression '^' and_expression
    ;

inclusive_or_expression
    : exclusive_or_expression
    | inclusive_or_expression '|' exclusive_or_expression
    ;

Jika operan operator logis memiliki jenis waktu kompilasi dynamic, maka ekspresi terikat secara dinamis (§12.3.3). Dalam kasus ini, jenis waktu kompilasi dari ekspresi tersebut adalah dynamic, dan resolusi yang dijelaskan di bawah ini akan terjadi saat runtime menggunakan jenis runtime dari operand yang memiliki jenis waktu kompilasi dynamic.

Untuk pengoperasian formulir x «op» y, di mana «op» adalah salah satu operator logis, resolusi kelebihan beban (§12.4.5) diterapkan untuk memilih implementasi operator tertentu. Operand dikonversi ke jenis parameter operator yang dipilih, dan jenis hasilnya adalah jenis pengembalian operator.

Operator logis yang telah ditentukan sebelumnya dijelaskan dalam subklaus berikut.

12.15.2 Operator logis bilangan bulat

Operator logika bilangan bulat yang telah ditentukan sebelumnya adalah:

int operator &(int x, int y);
uint operator &(uint x, uint y);
long operator &(long x, long y);
ulong operator &(ulong x, ulong y);

int operator |(int x, int y);
uint operator |(uint x, uint y);
long operator |(long x, long y);
ulong operator |(ulong x, ulong y);

int operator ^(int x, int y);
uint operator ^(uint x, uint y);
long operator ^(long x, long y);
ulong operator ^(ulong x, ulong y);

Operator & menghitung DAN logika bitwise dari dua operan, operator | menghitung ATAU logika bitwise dari dua operan, dan operator ^ menghitung ATAU logika eksklusif bitwise dari dua operan. Tidak ada luapan yang dimungkinkan dari operasi ini.

Bentuk angkat (§12.4.8) dari operator logis bilangan bulat yang telah ditentukan sebelumnya juga sudah ditentukan seperti yang dijelaskan di bagian atas.

12.15.3 Operator logis enumerasi

Setiap jenis enumerasi E secara implisit menyediakan operator logis yang telah ditentukan berikut:

E operator &(E x, E y);
E operator |(E x, E y);
E operator ^(E x, E y);

Hasil evaluasi x «op» y, di mana x dan y adalah ekspresi dari jenis enumerasi E dengan jenis yang mendasar U, dan «op» adalah salah satu operator logis, sama persis dengan mengevaluasi (E)((U)x «op» (U)y). Dengan kata lain, operator logis jenis enumerasi hanya melakukan operasi logis pada jenis mendasar dari dua operan.

Bentuk terangkat (§12.4.8) dari operator logika enumerasi yang telah ditentukan sebelumnya dan tidak terangkat seperti yang dijelaskan di atas juga telah ditentukan sebelumnya.

12.15.4 Operator logis Boolean

Operator logis Boolean yang telah ditentukan sebelumnya adalah:

bool operator &(bool x, bool y);
bool operator |(bool x, bool y);
bool operator ^(bool x, bool y);

Hasil dari x & y adalah true jika x dan y adalah true. Jika tidak, hasilnya false.

Hasil dari x | y adalah true jika x atau y adalah true. Jika tidak, hasilnya false.

Hasil dari x ^ y adalah true jika x adalah true dan y adalah false, atau x adalah false dan y adalah true. Jika tidak, hasilnya false. Ketika operand berjenis bool, operator ^ menghitung hasil yang sama dengan operator !=.

12.15.5 Boolean Nullable & dan | Operator

Jenis Boolean nullable bool? dapat mewakili tiga nilai, true, false, dan null.

Seperti operator biner lainnya, bentuk operator & logis yang diangkat dan | (§12.15.4) juga telah ditentukan sebelumnya:

bool? operator &(bool? x, bool? y);
bool? operator |(bool? x, bool? y);

Semantik operator & dan | yang diangkat ditentukan oleh tabel berikut:

x y x & y x \| y
true true true true
true false false true
true null null true
false true false true
false false false false
false null false null
null true null true
null false false null
null null null null

Note: Jenis bool? secara konseptual mirip dengan jenis tiga nilai yang digunakan untuk ekspresi Boolean di SQL. Tabel di atas mengikuti semantik yang sama dengan SQL, sedangkan menerapkan aturan §12.4.8 ke operator & dan | tidak akan. Aturan §12.4.8 sudah menyediakan semantik seperti SQL untuk operator ^ yang diangkat. catatan akhir

12.16 Operator logis kondisional

12.16.1 Umum

Operator && dan || disebut operator logis kondisional. Mereka juga disebut operator logis "pendek-sirkuit".

conditional_and_expression
    : inclusive_or_expression
    | conditional_and_expression '&&' inclusive_or_expression
    ;

conditional_or_expression
    : conditional_and_expression
    | conditional_or_expression '||' conditional_and_expression
    ;

Operator && dan || merupakan versi bersyarat dari operator & dan |.

  • Operasi x && y sesuai dengan operasi x & y, kecuali bahwa y dievaluasi hanya jika x tidak false.
  • Operasi x || y sesuai dengan operasi x | y, kecuali bahwa y dievaluasi hanya jika x tidak true.

Catatan: Alasan bahwa pemutusan rantai logika menggunakan kondisi 'bukan benar' dan 'bukan salah' adalah untuk memungkinkan operator kondisional yang ditentukan pengguna untuk menentukan kapan pemutusan rantai logika berlaku. Jenis yang ditentukan pengguna dapat berada dalam status di mana operator true mengembalikan false dan operator false mengembalikan false. Dalam kasus tersebut, baik && maupun || tidak akan mengalami hubung singkat. catatan akhir

Jika operan operator logis kondisional memiliki jenis waktu kompilasi dynamic, maka ekspresi terikat secara dinamis (§12.3.3). Dalam kasus ini, jenis waktu kompilasi dari ekspresi tersebut adalah dynamic, dan resolusi yang dijelaskan di bawah ini akan terjadi saat runtime menggunakan jenis runtime dari operand yang memiliki jenis waktu kompilasi dynamic.

Operasi formulir x && y atau x || y diproses dengan menerapkan resolusi kelebihan beban (§12,4,5) seolah-olah operasi ditulis x & y atau x | y. Kemudian

  • Jika resolusi kelebihan beban gagal menemukan operator terbaik tunggal, atau jika resolusi kelebihan beban memilih salah satu operator logis bilangan bulat yang telah ditentukan sebelumnya atau operator logis Boolean nullable (§12.15.5), kesalahan waktu pengikatan terjadi.
  • Jika tidak, jika operator yang dipilih adalah salah satu operator logis Boolean yang telah ditentukan sebelumnya (§12.15.4), operasi diproses seperti yang dijelaskan dalam §12.16.2.
  • Jika tidak, operator yang dipilih adalah operator yang ditentukan pengguna, dan operasi diproses seperti yang dijelaskan dalam §12.16.3.

Tidak dimungkinkan untuk secara langsung meng-overload operator logika kondisional. Namun, karena operator logis kondisional dievaluasi dalam hal operator logis reguler, overload dari operator logis reguler, dengan pembatasan tertentu, juga dianggap sebagai overload dari operator logis kondisional. Ini dijelaskan lebih lanjut dalam §12.16.3.

12.16.2 Operator logis bersyarat Boolean

Ketika operan && atau || berjenis bool, atau ketika operan adalah jenis yang tidak menentukan operator & atau operator |yang berlaku, tetapi menentukan konversi implisit ke bool, operasi diproses sebagai berikut:

  • Operasi x && y dievaluasi sebagai x ? y : false. Dengan kata lain, x pertama kali dievaluasi dan dikonversi ke jenis bool. Kemudian, jika xtrue, y dievaluasi dan dikonversi ke jenis bool, dan ini menjadi hasil operasi. Jika tidak, hasil operasi adalah false.
  • Operasi x || y dievaluasi sebagai x ? true : y. Dengan kata lain, x pertama kali dievaluasi dan dikonversi ke jenis bool. Kemudian, jika xtrue, hasil operasi adalah true. Jika tidak, y dievaluasi dan diubah menjadi tipe bool, dan ini menjadi hasil operasi.

12.16.3 Operator logis kondisional yang ditentukan pengguna

Ketika operan && atau || adalah tipe yang mendeklarasikan operator & atau operator |yang ditentukan pengguna dan relevan, kedua kondisi berikut harus benar, di mana T adalah tipe di mana operator yang dipilih dinyatakan:

  • Jenis pengembalian dan jenis setiap parameter operator yang dipilih harus T. Dengan kata lain, operator harus menghitung AND logika atau OR logika dari dua operan jenis T, dan harus mengembalikan hasil dari jenis T.
  • T akan berisi pernyataan operator true dan operator false.

Kesalahan waktu pengikatan terjadi jika salah satu persyaratan ini tidak terpenuhi. Jika tidak, operasi && atau || dievaluasi dengan menggabungkan operator true yang ditentukan pengguna atau operator false dengan operator yang ditentukan pengguna yang dipilih:

  • Operasi x && y dievaluasi sebagai T.false(x) ? x : T.&(x, y), di mana T.false(x) adalah pemanggilan operator false yang dideklarasikan dalam T, dan T.&(x, y) adalah pemanggilan operator &yang dipilih. Dengan kata lain, x pertama kali dievaluasi dan operator false dipanggil pada hasil untuk menentukan apakah x pasti salah. Kemudian, jika x pasti salah, hasil operasi adalah nilai yang sebelumnya dihitung untuk x. Jika tidak, y dievaluasi, kemudian operator & yang dipilih dipanggil dengan menggunakan nilai yang telah dihitung sebelumnya untuk x dan nilai yang dihitung untuk y untuk menghasilkan hasil dari operasi tersebut.
  • Operasi x || y dievaluasi sebagai T.true(x) ? x : T.|(x, y), di mana T.true(x) adalah pemanggilan operator true yang dideklarasikan dalam T, dan T.|(x, y) adalah pemanggilan operator |yang dipilih. Dengan kata lain, x pertama kali dievaluasi dan operator true dipanggil pada hasil untuk menentukan apakah x pasti benar. Kemudian, jika x pasti benar, hasil operasi adalah nilai yang sebelumnya dihitung untuk x. Jika tidak, y dievaluasi, kemudian operator | yang dipilih dipanggil dengan menggunakan nilai yang telah dihitung sebelumnya untuk x dan nilai yang dihitung untuk y untuk menghasilkan hasil dari operasi tersebut.

Dalam salah satu operasi ini, ekspresi yang diberikan oleh x dievaluasi hanya sekali, dan ekspresi yang diberikan oleh y tidak dievaluasi atau dievaluasi tepat sekali.

12.17 Operator coalescing null

Operator ?? disebut operator penggabungan null.

null_coalescing_expression
    : conditional_or_expression
    | conditional_or_expression '??' null_coalescing_expression
    | throw_expression
    ;

Dalam ekspresi null coalescing dalam bentuk a ?? b, jika a non-null, hasilnya adalah a; jika tidak, hasilnya b. Operasi mengevaluasi b hanya jika a adalah null.

Operator koalesensi null bersifat asosiatif kanan, yang berarti operasi dikelompokkan dari kanan ke kiri.

Contoh: Ekspresi formulir a ?? b ?? c dievaluasi sebagai a ?? (b ?? c). Secara umum, ekspresi dengan bentuk E1 ?? E2 ?? ... ?? EN mengembalikan operan pertama yang bukannull, atau null jika semua operan adalah null. contoh akhir

Jenis ekspresi a ?? b bergantung pada jenis konversi implisit yang tersedia pada operand. Dalam urutan preferensi, jenis a ?? b adalah A₀, A, atau B, di mana A adalah jenis a (asalkan a memiliki jenis), B adalah jenis b(asalkan b memiliki jenis), dan A₀ adalah jenis di balik A jika A adalah jenis nilai nullable, atau jika tidak, A. Secara khusus, a ?? b diproses sebagai berikut:

  • Jika A ada dan merupakan jenis yang tidak dikelola (§8,8) atau dikenal sebagai jenis nilai yang tidak dapat diubah ke null, kesalahan waktu kompilasi terjadi.
  • Jika tidak, jika A ada dan b adalah ekspresi dinamis, jenis hasilnya adalah dynamic. Saat runtime, a dievaluasi terlebih dahulu. Jika a tidak null, a dikonversi ke dynamic, dan ini menjadi hasilnya. Jika tidak, b dievaluasi, dan ini menjadi hasilnya.
  • Jika tidak, jika A ada dan merupakan tipe nilai yang dapat bernilai null dan konversi implisit ada dari b ke A₀, maka jenis hasilnya adalah A₀. Saat runtime, a dievaluasi terlebih dahulu. Jika a tidak null, a dibongkar untuk mengetik A₀, dan ini menjadi hasilnya. Jika tidak, b dievaluasi dan dikonversi ke jenis A₀, dan ini menjadi hasilnya.
  • Jika A ada dan konversi implisit dari b ke Aada, maka jenis hasilnya adalah A. Saat runtime, a dievaluasi terlebih dahulu. Jika a tidak null, a menjadi hasilnya. Jika tidak, b dievaluasi dan dikonversi ke jenis A, dan ini menjadi hasilnya.
  • Jika A ada dan merupakan tipe nilai yang dapat bernilai null, serta b memiliki tipe B dan ada konversi implisit dari A₀ ke B, maka tipe hasilnya adalah B. Saat runtime, a dievaluasi terlebih dahulu. Jika a tidak null, a diurai ke jenis A₀ dan dikonversi ke jenis B, dan ini menjadi hasilnya. Jika tidak, b dievaluasi dan menjadi hasilnya.
  • Sebaliknya, jika b memiliki jenis B dan konversi implisit ada dari a ke B, maka jenis hasilnya adalah B. Saat runtime, a dievaluasi terlebih dahulu. Jika a tidak null, a dikonversi ke jenis B, dan ini menjadi hasilnya. Jika tidak, b dievaluasi dan menjadi hasilnya.
  • Jika tidak, a dan b tidak kompatibel, dan terjadi kesalahan waktu kompilasi.

Contoh:

T M<T>(T a, T b) => a ?? b;

string s = M(null, "text");
int i = M(19, 23);

Parameter jenis T untuk metode M tidak dibatasi. Oleh karena itu, argumen jenis dapat berupa jenis referensi, atau jenis nilai nullable, seperti yang ditunjukkan dalam panggilan pertama ke M. Argumen jenis juga dapat berupa jenis nilai yang tidak dapat diubah ke null, seperti yang ditunjukkan dalam panggilan kedua ke M. Ketika argumen jenis adalah jenis nilai yang tidak dapat diubah ke null, nilai ekspresi a ?? b adalah a.

contoh akhir

12.18 Operator ekspresi lemparan

throw_expression
    : 'throw' null_coalescing_expression
    ;

throw_expression melemparkan nilai hasil evaluasi dari null_coalescing_expression. Ekspresi harus secara implisit dapat dikonversi ke System.Exception, dan hasil mengevaluasi ekspresi dikonversi ke System.Exception sebelum dilemparkan. Perilaku pada waktu runtime dari evaluasi ekspresi throw sama dengan yang ditentukan untuk throw statement (§13.10.6).

throw_expression tidak memiliki tipe. throw_expression dapat dikonversi ke setiap tipe melalui konversi lemparan implisit .

ekspresi lemparan hanya akan terjadi dalam konteks sintaksis berikut:

  • Sebagai operan kedua atau ketiga dari operator bersyarat terner (?:).
  • Sebagai operan kedua dari null coalescing operator (??).
  • Sebagai tubuh lambda atau anggota bertubuh ekspresi.

12.19 Ekspresi deklarasi

Ekspresi deklarasi mendeklarasikan variabel lokal.

declaration_expression
    : local_variable_type identifier
    ;

local_variable_type
    : type
    | 'var'
    ;

simple_name_ juga dianggap sebagai ekspresi deklarasi jika pencarian nama sederhana tidak menemukan deklarasi terkait (§12.8.4). Saat digunakan sebagai ekspresi deklarasi, _ disebut pembuangan sederhana. Ini secara semantik setara dengan var _, tetapi diizinkan di lebih banyak tempat.

Ekspresi deklarasi hanya akan terjadi dalam konteks syntactic berikut:

  • Sebagai outnilai_argumen dalam daftar_argumen.
  • Sebagai pembuangan _ sederhana yang terdiri dari sisi kiri tugas sederhana (§12.23.2).
  • Sebagai tuple_element dalam satu atau beberapa tuple_expressionyang berlapis secara rekursif, yang paling luar merupakan sisi kiri dari penugasan mendekonstruksi. Meskipun ekspresi deklarasi tidak ada secara sintaksis, deconstruction_expression memunculkan ekspresi deklarasi dalam posisi ini.

Catatan: Ini berarti bahwa ekspresi deklarasi tidak dapat berada dalam tanda kurung. catatan akhir

Merupakan kesalahan jika variabel yang diketik secara implisit dan dideklarasikan dengan declaration_expression dirujuk dalam argument_list di mana variabel tersebut dideklarasikan.

Ini adalah kesalahan untuk variabel yang dideklarasikan dengan declaration_expression yang direferensikan dalam penugasan dekontruksi di mana itu terjadi.

Ekspresi deklarasi yang merupakan pembuangan sederhana atau di mana local_variable_type adalah pengidentifikasi var, diklasifikasikan sebagai variabel yang diketik secara implisit . Ekspresi tidak memiliki jenis, dan jenis variabel lokal disimpulkan berdasarkan konteks syntactic sebagai berikut:

  • Dalam argument_list jenis variabel yang disimpulkan adalah jenis yang dideklarasikan dari parameter yang sesuai.
  • Sebagai sisi kiri penugasan sederhana, jenis variabel yang disimpulkan adalah jenis sisi kanan penugasan.
  • Dalam tuple_expression di sisi kiri penetapan sederhana, tipe variabel yang disimpulkan adalah tipe elemen tuple yang bersesuaian di sisi kanan (setelah dekonstruksi) dari penetapan tersebut.

Jika tidak, ekspresi deklarasi diklasifikasikan sebagai variabel yang diketik secara eksplisit , dan jenis ekspresi serta variabel yang dideklarasikan harus yang diberikan oleh local_variable_type.

Ekspresi deklarasi dengan pengenal _ adalah pengabaian (§9.2.9.2), dan tidak memberikan nama kepada variabel. Ekspresi deklarasi dengan pengidentifikasi selain _ memasukkan nama tersebut ke dalam ruang deklarasi variabel lokal terdekat yang menutup (§7.3).

Contoh:

string M(out int i, string s, out bool b) { ... }

var s1 = M(out int i1, "One", out var b1);
Console.WriteLine($"{i1}, {b1}, {s1}");
// Error: i2 referenced within declaring argument list
var s2 = M(out var i2, M(out i2, "Two", out bool b2), out b2);
var s3 = M(out int _, "Three", out var _);

Deklarasi s1 menunjukkan ekspresi deklarasi yang diketik secara eksplisit dan implisit. Jenis yang disimpulkan dari b1 adalah bool karena ini adalah jenis parameter output yang sesuai dalam M1. WriteLine berikutnya dapat mengakses i1 dan b1, yang telah diperkenalkan dalam ruang lingkup tercakup.

Deklarasi s2 menunjukkan upaya untuk menggunakan i2 dalam panggilan berlapis ke M, yang tidak diizinkan, karena referensi terjadi dalam daftar argumen tempat i2 dideklarasikan. Di sisi lain referensi ke b2 dalam argumen akhir diizinkan, karena terjadi setelah akhir daftar argumen berlapis tempat b2 dideklarasikan.

Deklarasi s3 menunjukkan penggunaan ekspresi deklarasi yang diketik secara implisit dan eksplisit yang dibuang. Karena pembuangan tidak mendeklarasikan variabel dengan nama, beberapa kali kemunculan pengidentifikasi _ diizinkan.

(int i1, int _, (var i2, var _), _) = (1, 2, (3, 4), 5);

Contoh ini menunjukkan penggunaan frasa deklarasi yang diketik secara implisit dan eksplisit untuk variabel dan elemen yang diabaikan dalam penugasan dekontruksi. simple_name_ setara dengan var _ ketika tidak ada deklarasi _ yang ditemukan.

void M1(out int i) { ... }

void M2(string _)
{
    M1(out _);      // Error: `_` is a string
    M1(out var _);
}

Contoh ini menunjukkan penggunaan var _ untuk menyediakan penghapusan yang memiliki tipe secara implisit ketika _ tidak tersedia, karena _ menetapkan variabel dalam lingkup yang melingkupinya.

contoh akhir

12.20 Operator bersyar

Operator ?: disebut operator kondisional. Kadang-kadang ini juga disebut operator ternary.

conditional_expression
    : null_coalescing_expression
    | null_coalescing_expression '?' expression ':' expression
    | null_coalescing_expression '?' 'ref' variable_reference ':'
      'ref' variable_reference
    ;

Ekspresi lemparan (§12.18) tidak diizinkan dalam operator bersyarah jika ref ada.

Ekspresi kondisional formulir b ? x : y pertama kali mengevaluasi kondisi b. Kemudian, jika b adalah true, x dievaluasi untuk menjadi hasil operasi. Jika tidak, y dievaluasi dan menjadi hasil operasi. Ekspresi kondisional tidak pernah mengevaluasi x dan y.

Operator kondisional bersifat asosiatif kanan, yang berarti bahwa operasi dikelompokkan dari kanan ke kiri.

Contoh: Ekspresi formulir a ? b : c ? d : e dievaluasi sebagai a ? b : (c ? d : e). contoh akhir

Operan pertama operator ?: adalah ekspresi yang dapat dikonversi secara implisit ke bool, atau ekspresi jenis yang mengimplementasikan operator true. Jika tidak ada persyaratan ini yang terpenuhi, kesalahan waktu kompilasi terjadi.

Jika ref ada:

  • Konversi identitas harus ada di antara jenis dari dua variabel variable_reference, dan jenis hasilnya dapat berupa salah satu dari jenis tersebut. Jika salah satu jenis adalah dynamic, inferensi tipe lebih memilih dynamic (§8.7). Jika salah satu jenis adalah jenis tuple (§8.3.11), inferensi jenis menyertakan nama elemen ketika nama elemen ada pada posisi ordinal yang sama di kedua tuple.
  • Hasilnya adalah referensi variabel, yang dapat ditulis jika kedua variable_referencedapat ditulis.

Catatan: Saat ref ada, conditional_expression mengembalikan referensi variabel, yang dapat ditetapkan ke variabel referensi menggunakan operator = ref atau diteruskan sebagai parameter referensi/input/output. catatan akhir

Jika ref tidak ada, operand kedua dan ketiga, x dan y, dari operator ?: mengontrol jenis ekspresi kondisional.

  • Jika x memiliki jenis X dan y memiliki jenis Y,
    • Jika konversi identitas ada antara X dan Y, hasilnya adalah jenis ekspresi umum terbaik (§12.6.3.16). Jika salah satu jenis adalah dynamic, inferensi tipe lebih memilih dynamic (§8.7). Jika salah satu jenis adalah jenis tuple (§8.3.11), inferensi jenis menyertakan nama elemen ketika nama elemen ada pada posisi ordinal yang sama di kedua tuple.
    • Jika tidak, jika konversi implisit (§10,2) ada dari X ke Y, tetapi tidak dari Y ke X, maka Y adalah jenis ekspresi bersyarkat.
    • Jika tidak, jika konversi enumerasi implisit (§10,2,4) ada dari X ke Y, maka Y adalah jenis ekspresi kondisional.
    • Jika tidak, jika konversi enumerasi implisit (§10,2,4) ada dari Y ke X, maka X adalah jenis ekspresi kondisional.
    • Jika tidak, jika konversi implisit (§10,2) ada dari Y ke X, tetapi tidak dari X ke Y, maka X adalah jenis ekspresi bersyarkat.
    • Jika tidak, tidak ada jenis ekspresi yang dapat ditentukan, dan terjadi kesalahan waktu kompilasi.
  • Jika hanya salah satu x dan y yang memiliki jenis, dan x dan y secara implisit dapat dikonversi ke jenis tersebut, maka itu adalah jenis ekspresi kondisional.
  • Jika tidak, tidak ada jenis ekspresi yang dapat ditentukan, dan terjadi kesalahan waktu kompilasi.

Pemrosesan saat jalan dari ekspresi kondisional ref berbentuk b ? ref x : ref y terdiri dari langkah-langkah berikut:

  • Pertama, b dievaluasi, dan nilai boolb ditentukan:
    • Jika konversi implisit dari jenis b ke bool ada, konversi implisit ini dilakukan untuk menghasilkan nilai bool.
    • Jika tidak, operator true yang ditentukan oleh jenis b dipanggil untuk menghasilkan nilai bool.
  • Jika nilai bool yang dihasilkan oleh langkah di atas adalah true, maka x akan dievaluasi dan referensi variabel yang dihasilkan akan menjadi hasil dari ekspresi bersyarat.
  • Jika tidak, y dievaluasi dan referensi variabel yang dihasilkan menjadi hasil ekspresi kondisional.

Pemrosesan run-time ekspresi kondisional formulir b ? x : y terdiri dari langkah-langkah berikut:

  • Pertama, b dievaluasi, dan nilai boolb ditentukan:
    • Jika konversi implisit dari jenis b ke bool ada, konversi implisit ini dilakukan untuk menghasilkan nilai bool.
    • Jika tidak, operator true yang ditentukan oleh jenis b dipanggil untuk menghasilkan nilai bool.
  • Jika nilai bool yang dihasilkan oleh langkah di atas true, maka x dievaluasi dan dikonversi ke jenis ekspresi kondisional, dan ini menjadi hasil ekspresi kondisional.
  • Jika tidak, y dievaluasi dan dikonversi ke jenis ekspresi kondisional, dan ini menjadi hasil ekspresi kondisional.

12.21 Ekspresi fungsi anonim

12.21.1 Umum

Fungsi anonim adalah ekspresi yang mewakili definisi metode "sebaris". Fungsi anonim tidak memiliki nilai atau jenis dalam dan dari dirinya sendiri, tetapi dapat dikonversi ke delegasi yang sesuai atau tipe pohon ekspresi. Evaluasi konversi fungsi anonim tergantung pada tipe target konversi: Jika merupakan tipe delegasi, konversi akan bernilai delegasi yang merujuk pada metode yang ditentukan oleh fungsi anonim. Jika ini adalah jenis pohon ekspresi, konversi dievaluasi menjadi pohon ekspresi yang mewakili struktur metode sebagai struktur objek.

Catatan: Untuk alasan historis, ada dua rasa sintaks fungsi anonimnya, yaitu lambda_expression's dan anonymous_method_expression's. Untuk hampir semua tujuan, lambda_expression lebih ringkas dan ekspresif daripada anonymous_method_expression, yang tetap dalam bahasa untuk kompatibilitas ke belakang. catatan akhir

lambda_expression
    : 'async'? anonymous_function_signature '=>' anonymous_function_body
    ;

anonymous_method_expression
    : 'async'? 'delegate' explicit_anonymous_function_signature? block
    ;

anonymous_function_signature
    : explicit_anonymous_function_signature
    | implicit_anonymous_function_signature
    ;

explicit_anonymous_function_signature
    : '(' explicit_anonymous_function_parameter_list? ')'
    ;

explicit_anonymous_function_parameter_list
    : explicit_anonymous_function_parameter
      (',' explicit_anonymous_function_parameter)*
    ;

explicit_anonymous_function_parameter
    : anonymous_function_parameter_modifier? type identifier
    ;

anonymous_function_parameter_modifier
    : 'ref'
    | 'out'
    | 'in'
    ;

implicit_anonymous_function_signature
    : '(' implicit_anonymous_function_parameter_list? ')'
    | implicit_anonymous_function_parameter
    ;

implicit_anonymous_function_parameter_list
    : implicit_anonymous_function_parameter
      (',' implicit_anonymous_function_parameter)*
    ;

implicit_anonymous_function_parameter
    : identifier
    ;

anonymous_function_body
    : null_conditional_invocation_expression
    | expression
    | 'ref' variable_reference
    | block
    ;

Saat mengenali anonymous_function_body, jika baik ekspresi null_conditional_invocation_expression maupun alternatif berlaku, maka yang pertama akan dipilih.

Catatan: Tumpang tindih dan prioritas antara alternatif di sini hanyalah demi kenyamanan penjelasan; aturan tata bahasa dapat diperluas untuk menghilangkan tumpang tindih tersebut. ANTLR, dan sistem tata bahasa lainnya, mengadopsi kenyamanan yang sama sehingga anonymous_function_body memiliki semantik yang ditentukan secara otomatis. catatan akhir

Catatan: Saat diperlakukan sebagai ekspresi , bentuk sintaksis seperti x?.M() akan menjadi kesalahan jika tipe hasil dari M adalah void (§12.8.13). Tetapi ketika diperlakukan sebagai null_conditional_invocation_expression, tipe hasil boleh void. catatan akhir

Contoh: Jenis hasil dari List<T>.Reverse adalah void. Dalam kode berikut, isi ekspresi anonim adalah null_conditional_invocation_expression, karena itu bukan kesalahan.

Action<List<int>> a = x => x?.Reverse();

contoh akhir

Operator => memiliki prioritas yang sama dengan penugasan (=) dan bersifat asosiatif kanan.

Fungsi anonim dengan pengubah async adalah fungsi asinkron dan mengikuti aturan yang dijelaskan dalam §15.14.

Parameter fungsi anonim dalam bentuk lambda_expression dapat ditik secara eksplisit atau implisit. Dalam daftar parameter yang ditik secara eksplisit, jenis setiap parameter secara eksplisit dinyatakan. Dalam daftar parameter yang diketik secara implisit, jenis parameter disimpulkan dari konteks di mana fungsi anonim terjadi—khususnya, ketika fungsi anonim dikonversi ke jenis delegasi atau jenis pohon ekspresi yang kompatibel, jenis tersebut menyediakan jenis parameter (§10,7).

Dalam lambda_expression dengan parameter tunggal yang ditik secara implisit, tanda kurung dapat dihilangkan dari daftar parameter. Dengan kata lain, fungsi formulir anonim

( «param» ) => «expr»

dapat disingkat ke

«param» => «expr»

Daftar parameter fungsi anonim dalam bentuk anonymous_method_expression bersifat opsional. Jika diberikan, parameter harus ditik secara eksplisit. Jika tidak, fungsi anonim dapat dikonversi ke delegasi dengan daftar parameter apa pun yang tidak berisi parameter output.

Sebuah blok dari isi fungsi anonim selalu dapat dijangkau (§13.2).

Contoh: Beberapa contoh fungsi anonim mengikuti di bawah ini:

x => x + 1                             // Implicitly typed, expression body
x => { return x + 1; }                 // Implicitly typed, block body
(int x) => x + 1                       // Explicitly typed, expression body
(int x) => { return x + 1; }           // Explicitly typed, block body
(x, y) => x * y                        // Multiple parameters
() => Console.WriteLine()              // No parameters
async (t1,t2) => await t1 + await t2   // Async
delegate (int x) { return x + 1; }     // Anonymous method expression
delegate { return 1 + 1; }             // Parameter list omitted

contoh akhir

Perilaku lambda_expression-s dan anonymous_method_expression-s sama kecuali pada poin-poin berikut:

  • anonymous_method_expressionmengizinkan daftar parameter dihilangkan sepenuhnya, menghasilkan konvertibilitas untuk mendelegasikan jenis daftar parameter nilai apa pun.
  • lambda_expressionmengizinkan jenis parameter untuk dihilangkan dan disimpulkan sedangkan anonymous_method_expressionmengharuskan jenis parameter dinyatakan secara eksplisit.
  • Isi lambda_expression bisa berupa ekspresi atau blok, sementara isi anonymous_method_expression harus berupa blok.
  • Hanya lambda_expressionyang memiliki konversi ke jenis pohon ekspresi yang kompatibel (§8,6).

12.21.2 Tanda tangan fungsi anonim

anonymous_function_signature dari fungsi anonim mengatur nama-nama dan secara opsional tipe-tipe parameter untuk fungsi anonim. Cakupan parameter fungsi anonim adalah anonymous_function_body (§7,7). Bersama dengan daftar parameter (jika diberikan) isi metode anonim merupakan ruang deklarasi (§7,3). Dengan demikian, ini adalah kesalahan waktu kompilasi untuk nama parameter fungsi anonim agar sesuai dengan nama variabel lokal, konstanta lokal atau parameter yang cakupannya mencakup anonymous_method_expression atau lambda_expression.

Jika fungsi anonim memiliki explicit_anonymous_function_signature, maka kumpulan jenis delegasi yang kompatibel dan jenis pohon ekspresi dibatasi untuk yang memiliki jenis parameter dan pengubah yang sama dalam urutan yang sama (§10,7). Berbeda dengan konversi grup metode (§10.8), kontravarians parameter jenis fungsi anonim tidak didukung. Jika fungsi anonim tidak memiliki anonymous_function_signature, maka kumpulan jenis delegasi yang kompatibel dan jenis pohon ekspresi dibatasi untuk yang tidak memiliki parameter output.

Perhatikan bahwa anonymous_function_signature tidak dapat menyertakan atribut atau array parameter. Namun demikian, anonymous_function_signature mungkin kompatibel dengan jenis delegasi yang daftar parameternya berisi array parameter.

Perhatikan juga bahwa konversi ke jenis pohon ekspresi, bahkan jika kompatibel, mungkin masih gagal pada waktu kompilasi (§8,6).

12.21.3 Badan fungsi anonim

Isi (ekspresi atau blok) dari fungsi anonim tunduk pada aturan berikut:

  • Jika fungsi anonim menyertakan tanda tangan, parameter yang ditentukan dalam tanda tangan tersedia dalam isi. Jika fungsi anonim tidak memiliki tanda tangan, fungsi tersebut dapat dikonversi ke jenis delegasi atau jenis ekspresi yang memiliki parameter (§10,7), tetapi parameter tidak dapat diakses dalam isi.
  • Kecuali untuk parameter referensi yang ditentukan dalam tanda tangan (jika ada) dari fungsi anonim terdekat, akan terjadi kesalahan waktu kompilasi jika badan mencoba mengakses parameter referensi.
  • Kecuali untuk parameter yang ditentukan dalam deklarasi (jika ada) dari fungsi anonim terdekat yang menyertakan, ini adalah kesalahan waktu kompilasi jika isi mengakses parameter dari jenis ref struct.
  • Ketika tipe this adalah tipe struktur, terjadinya kesalahan pada waktu kompilasi jika bagian tersebut mencoba mengakses this. Ini benar apakah aksesnya eksplisit (seperti dalam this.x) atau implisit (seperti pada x di mana x adalah anggota instans dari struct). Aturan ini hanya melarang akses tersebut dan tidak memengaruhi apakah pencarian anggota menghasilkan anggota struktur.
  • Isi memiliki akses ke variabel luar (§12.21.6) dari fungsi anonim. Akses variabel luar akan mereferensikan instans variabel yang aktif pada saat lambda_expression atau anonymous_method_expression dievaluasi (§12.21.7).
  • Ini adalah kesalahan waktu kompilasi jika badan berisi pernyataan goto, pernyataan break, atau pernyataan continue yang targetnya berada di luar badan atau berada dalam badan fungsi anonim yang terkandung.
  • Pernyataan return di dalam badan mengembalikan kontrol dari panggilan fungsi anonim pembungkus terdekat, bukan dari anggota fungsi pembungkus.

Secara eksplisit tidak ditentukan apakah ada cara untuk menjalankan blok fungsi anonim selain melalui evaluasi dan pemanggilan lambda_expression atau anonymous_method_expression. Secara khusus, pengompilasi dapat memilih untuk menerapkan fungsi anonim dengan mensintesis satu atau beberapa metode atau jenis bernama. Nama-nama elemen yang disintesis tersebut harus berupa formulir yang disediakan untuk penggunaan kompilator (§6,4,3).

12.21.4 Resolusi kelebihan beban

Fungsi anonim dalam daftar argumen berpartisipasi dalam inferensi jenis dan resolusi kelebihan beban. Lihat §12.6.3 dan §12.6.4 untuk aturan yang tepat.

Contoh: Contoh berikut mengilustrasikan efek fungsi anonim pada resolusi kelebihan beban.

class ItemList<T> : List<T>
{
    public int Sum(Func<T, int> selector)
    {
        int sum = 0;
        foreach (T item in this)
        {
            sum += selector(item);
        }
        return sum;
    }

    public double Sum(Func<T, double> selector)
    {
        double sum = 0;
        foreach (T item in this)
        {
            sum += selector(item);
        }
        return sum;
    }
}

Kelas ItemList<T> memiliki dua metode Sum. Masing-masing mengambil sebuah argumen selector, yang mengekstrak nilai untuk dijumlahkan dari setiap item dalam daftar. Nilai yang diekstrak dapat berupa int atau double dan jumlah yang dihasilkan juga merupakan int atau double.

Metode Sum misalnya dapat digunakan untuk menghitung jumlah dari daftar baris detail dalam urutan.

class Detail
{
    public int UnitCount;
    public double UnitPrice;
    ...
}

class A
{
    void ComputeSums()
    {
        ItemList<Detail> orderDetails = GetOrderDetails( ... );
        int totalUnits = orderDetails.Sum(d => d.UnitCount);
        double orderTotal = orderDetails.Sum(d => d.UnitPrice * d.UnitCount);
        ...
    }

    ItemList<Detail> GetOrderDetails( ... )
    {
        ...
    }
}

Dalam pemanggilan pertama orderDetails.Sum, kedua metode Sum berlaku karena fungsi anonim d => d.UnitCount kompatibel dengan Func<Detail,int> dan Func<Detail,double>. Namun, resolusi kelebihan beban memilih metode Sum pertama karena konversi ke Func<Detail,int> lebih baik daripada konversi ke Func<Detail,double>.

Dalam pemanggilan kedua orderDetails.Sum, hanya metode Sum kedua yang berlaku karena fungsi anonim d => d.UnitPrice * d.UnitCount menghasilkan nilai jenis double. Dengan demikian, resolusi kelebihan beban memilih metode Sum kedua untuk pemanggilan tersebut.

contoh akhir

12.21.5 Fungsi anonim dan pengikatan dinamis

Fungsi anonim tidak boleh menjadi penerima, argumen, atau operand dari operasi yang terikat secara dinamis.

12.21.6 Variabel luar

12.21.6.1 Umum

Setiap variabel lokal, parameter berdasar nilai, atau array parameter yang cakupannya mencakup lambda_expression atau anonymous_method_expression disebut sebagai variabel luar oleh fungsi anonim. Dalam contoh anggota fungsi kelas, nilai ini dianggap sebagai parameter nilai dan merupakan variabel luar dari fungsi anonim apa pun yang terkandung dalam anggota fungsi.

12.21.6.2 Variabel luar yang ditangkap

Ketika variabel luar direferensikan oleh fungsi anonim, variabel luar dikatakan telah ditangkap oleh fungsi anonim. Biasanya, masa pakai variabel lokal terbatas pada eksekusi blok atau pernyataan yang terkait dengannya (§9.2.9.1). Namun, masa pakai variabel luar yang ditangkap diperpanjang setidaknya sampai delegasi atau pohon ekspresi yang dibuat dari fungsi anonim menjadi memenuhi syarat untuk pengumpulan sampah.

Contoh: Dalam contoh

delegate int D();

class Test
{
    static D F()
    {
        int x = 0;
        D result = () => ++x;
        return result;
    }

    static void Main()
    {
        D d = F();
        Console.WriteLine(d());
        Console.WriteLine(d());
        Console.WriteLine(d());
    }
}

variabel lokal x ditangkap oleh fungsi anonim, dan masa pakai x diperpanjang setidaknya sampai delegasi yang dikembalikan dari F menjadi memenuhi syarat untuk pengumpulan sampah. Karena setiap pemanggilan fungsi anonim beroperasi pada instans xyang sama , output contohnya adalah:

1
2
3

contoh akhir

Ketika variabel lokal atau parameter nilai ditangkap oleh fungsi anonim, variabel atau parameter lokal tidak lagi dianggap sebagai variabel tetap (§24.4), tetapi dianggap sebagai variabel yang dapat dipindahkan. Namun, variabel luar yang diambil tidak dapat digunakan dalam fixed pernyataan (§24,7), sehingga alamat variabel luar yang diambil tidak dapat diambil.

Catatan: Tidak seperti variabel yang tidak ditangkap, variabel lokal yang diambil dapat secara bersamaan diekspos ke beberapa utas eksekusi. catatan akhir

12.21.6.3 Instansiasi variabel lokal

Variabel lokal dianggap diinstansiasi saat eksekusi memasuki cakupan variabel.

Contoh: Misalnya, ketika metode berikut dipanggil, variabel lokal x dibuat dan diinisialisasi tiga kali—sekali untuk setiap perulangan perulangan.

static void F()
{
    for (int i = 0; i < 3; i++)
    {
        int x = i * 2 + 1;
        ...
    }
}

Namun, jika deklarasi x dipindahkan ke luar perulangan, itu akan menghasilkan satu instansiasi x:

static void F()
{
    int x;
    for (int i = 0; i < 3; i++)
    {
        x = i * 2 + 1;
        ...
    }
}

contoh akhir

Ketika tidak ditangkap, tidak ada cara untuk mengamati dengan tepat seberapa sering variabel lokal dibuat—karena masa pakai instans terputus-putus, dimungkinkan bagi setiap instansiasi untuk hanya menggunakan lokasi penyimpanan yang sama. Namun, ketika fungsi anonim menangkap variabel lokal, efek instansiasi menjadi jelas.

Contoh: Contoh

delegate void D();
class Test
{
    static D[] F()
    {
        D[] result = new D[3];
        for (int i = 0; i < 3; i++)
        {
            int x = i * 2 + 1;
            result[i] = () => Console.WriteLine(x);
        }
        return result;
    }

    static void Main()
    {
        foreach (D d in F())
        {
            d();
        }
    }
}

menghasilkan output:

1
3
5

Namun, ketika deklarasi x dipindahkan di luar perulangan:

delegate void D();

class Test
{
    static D[] F()
    {
        D[] result = new D[3];
        int x;
        for (int i = 0; i < 3; i++)
        {
            x = i * 2 + 1;
            result[i] = () => Console.WriteLine(x);
        }
        return result;
   }

   static void Main()
   {
       foreach (D d in F())
       {
           d();
       }
   }
}

outputnya adalah:

5
5
5

Perhatikan bahwa kompilator diizinkan (tetapi tidak diperlukan) untuk mengoptimalkan tiga instans ke dalam satu instans delegasi (§10,7,2).

contoh akhir

Jika for-loop menyatakan variabel iterasi, variabel itu sendiri dianggap dideklarasikan di luar perulangan.

Contoh: Dengan demikian, jika contoh diubah untuk mengambil variabel iterasi itu sendiri:

delegate void D();

class Test
{
    static D[] F()
    {
        D[] result = new D[3];
        for (int i = 0; i < 3; i++)
        {
            result[i] = () => Console.WriteLine(i);
        }
        return result;
   }

   static void Main()
   {
       foreach (D d in F())
       {
           d();
       }
   }
}

hanya satu instans variabel iterasi yang ditangkap, yang menghasilkan output:

3
3
3

contoh akhir

Dimungkinkan bagi delegasi fungsi anonim untuk berbagi beberapa variabel yang ditangkap tetapi memiliki instans terpisah dari yang lain.

Contoh: Misalnya, jika F diubah menjadi

static D[] F()
{
    D[] result = new D[3];
    int x = 0;
    for (int i = 0; i < 3; i++)
    {
        int y = 0;
        result[i] = () => Console.WriteLine($"{++x} {++y}");
    }
    return result;
}

Ketiga delegasi menangkap instans x yang sama namun instans terpisah dari y, dan outputnya adalah:

1 1
2 1
3 1

contoh akhir

Fungsi anonim terpisah dapat menangkap instans variabel luar yang sama.

Contoh: Dalam contoh:

delegate void Setter(int value);
delegate int Getter();

class Test
{
    static void Main()
    {
        int x = 0;
        Setter s = (int value) => x = value;
        Getter g = () => x;
        s(5);
        Console.WriteLine(g());
        s(10);
        Console.WriteLine(g());
    }
}

dua fungsi anonim menangkap instans variabel lokal yang sama x, dan dengan demikian mereka dapat "berkomunikasi" melalui variabel tersebut. Output contohnya adalah:

5
10

contoh akhir

12.21.7 Evaluasi ekspresi fungsi anonim

Fungsi anonim F harus selalu dikonversi menjadi tipe delegasi D atau tipe pohon ekspresi E, baik secara langsung atau melalui eksekusi ekspresi pembuatan delegasi new D(F). Konversi ini menentukan hasil fungsi anonim, seperti yang dijelaskan dalam §10,7.

Contoh Implementasi 12.21.8

Subklaus ini bersifat informatif.

Subklasul ini menjelaskan kemungkinan implementasi konversi fungsi anonim dalam hal konstruksi C# lainnya. Implementasi yang dijelaskan di sini didasarkan pada prinsip yang sama yang digunakan oleh pengompilasi C# komersial, tetapi tidak berarti implementasi yang diamanatkan, juga bukan satu-satunya yang mungkin. Ini hanya secara singkat menyebutkan konversi ke pohon ekspresi, karena makna tepatnya berada di luar cakupan spesifikasi ini.

Sisa subklasul ini memberikan beberapa contoh kode yang berisi fungsi anonim dengan karakteristik yang berbeda. Untuk setiap contoh, tersedia terjemahan yang sesuai ke dalam kode yang hanya menggunakan konstruksi C# lainnya. Dalam contoh, pengidentifikasi D diasumsikan mewakili jenis delegasi berikut:

public delegate void D();

Bentuk paling sederhana dari fungsi anonim adalah yang tidak menangkap variabel luar:

delegate void D();

class Test
{
    static void F()
    {
        D d = () => Console.WriteLine("test");
    }
}

Ini dapat diterjemahkan ke instansiasi delegasi yang mereferensikan metode statis yang dihasilkan kompilator di mana kode fungsi anonim ditempatkan:

delegate void D();

class Test
{
    static void F()
    {
        D d = new D(__Method1);
    }

    static void __Method1()
    {
        Console.WriteLine("test");
    }
}

Dalam contoh berikut, fungsi anonim mereferensikan anggota instans this:

delegate void D();

class Test
{
    int x;

    void F()
    {
        D d = () => Console.WriteLine(x);
    }
}

Ini dapat diterjemahkan ke metode instans yang dihasilkan kompilator yang berisi kode fungsi anonim:

delegate void D();

class Test
{
   int x;

   void F()
   {
       D d = new D(__Method1);
   }

   void __Method1()
   {
       Console.WriteLine(x);
   }
}

Dalam contoh ini, fungsi anonim menangkap variabel lokal:

delegate void D();

class Test
{
    void F()
    {
        int y = 123;
        D d = () => Console.WriteLine(y);
    }
}

Masa pakai variabel lokal sekarang harus diperpanjang hingga setidaknya masa pakai delegasi fungsi anonim. Ini dapat dicapai dengan "menaikkan" variabel lokal ke dalam bidang kelas yang dihasilkan kompilator. Instans variabel lokal (§12.21.6.3) kemudian sesuai dengan pembuatan instans kelas yang dihasilkan kompilator, dan mengakses variabel lokal sesuai dengan mengakses bidang dalam instans kelas yang dihasilkan kompiler. Selain itu, fungsi anonim menjadi metode instans dari kelas yang dihasilkan kompilator:

delegate void D();

class Test
{
    void F()
    {
        __Locals1 __locals1 = new __Locals1();
        __locals1.y = 123;
        D d = new D(__locals1.__Method1);
    }

    class __Locals1
    {
        public int y;

        public void __Method1()
        {
            Console.WriteLine(y);
        }
    }
}

Akhirnya, fungsi anonim berikut menangkap this serta dua variabel lokal dengan masa pakai yang berbeda:

delegate void D();

class Test
{
   int x;

   void F()
   {
       int y = 123;
       for (int i = 0; i < 10; i++)
       {
           int z = i * 2;
           D d = () => Console.WriteLine(x + y + z);
       }
   }
}

Di sini, kelas yang dihasilkan kompilator dibuat untuk setiap blok di mana variabel lokal ditangkap sehingga variabel lokal di blok yang berbeda dapat memiliki masa hidup yang independen. Instans __Locals2, kelas yang dihasilkan kompilator untuk blok dalam, berisi variabel lokal z dan bidang yang mereferensikan instans __Locals1. Instans __Locals1, kelas yang dihasilkan kompilator untuk blok luar, berisi variabel lokal y dan bidang yang mereferensikan this anggota fungsi penutup. Dengan struktur data ini, dimungkinkan untuk menjangkau semua variabel luar yang ditangkap melalui instans __Local2, dan kode fungsi anonim dengan demikian dapat diimplementasikan sebagai metode instans kelas tersebut.

delegate void D();

class Test
{
    int x;

    void F()
    {
        __Locals1 __locals1 = new __Locals1();
        __locals1.__this = this;
        __locals1.y = 123;
        for (int i = 0; i < 10; i++)
        {
            __Locals2 __locals2 = new __Locals2();
            __locals2.__locals1 = __locals1;
            __locals2.z = i * 2;
            D d = new D(__locals2.__Method1);
        }
    }

    class __Locals1
    {
        public Test __this;
        public int y;
    }

    class __Locals2
    {
        public __Locals1 __locals1;
        public int z;

        public void __Method1()
        {
            Console.WriteLine(__locals1.__this.x + __locals1.y + z);
        }
    }
}

Teknik yang sama yang diterapkan di sini untuk menangkap variabel lokal juga dapat digunakan saat mengonversi fungsi anonim ke pohon ekspresi: referensi ke objek yang dihasilkan kompiler dapat disimpan di pohon ekspresi, dan akses ke variabel lokal dapat diwakili sebagai akses bidang pada objek ini. Keuntungan dari pendekatan ini adalah memungkinkan variabel lokal "diangkat" dibagikan antara delegasi dan pohon ekspresi.

Akhir teks informatif.

12.22 Ekspresi kueri

12.22.1 Umum

Ekspresi kueri menyediakan sintaksis terintegrasi bahasa untuk kueri yang mirip dengan bahasa kueri relasional dan hierarkis seperti SQL dan XQuery.

query_expression
    : from_clause query_body
    ;

from_clause
    : 'from' type? identifier 'in' expression
    ;

query_body
    : query_body_clause* select_or_group_clause query_continuation?
    ;

query_body_clause
    : from_clause
    | let_clause
    | where_clause
    | join_clause
    | join_into_clause
    | orderby_clause
    ;

let_clause
    : 'let' identifier '=' expression
    ;

where_clause
    : 'where' boolean_expression
    ;

join_clause
    : 'join' type? identifier 'in' expression 'on' expression
      'equals' expression
    ;

join_into_clause
    : 'join' type? identifier 'in' expression 'on' expression
      'equals' expression 'into' identifier
    ;

orderby_clause
    : 'orderby' orderings
    ;

orderings
    : ordering (',' ordering)*
    ;

ordering
    : expression ordering_direction?
    ;

ordering_direction
    : 'ascending'
    | 'descending'
    ;

select_or_group_clause
    : select_clause
    | group_clause
    ;

select_clause
    : 'select' expression
    ;

group_clause
    : 'group' expression 'by' expression
    ;

query_continuation
    : 'into' identifier query_body
    ;

Ekspresi kueri dimulai dengan klausa from dan diakhir dengan klausa select atau group. Klausa from awal dapat diikuti dengan nol atau lebih from, let, where, join atau orderby klausa. Setiap klausa from adalah generator yang memperkenalkan variabel rentang yang berkisar di atas elemen urutan . Setiap klausa let memperkenalkan variabel rentang yang mewakili nilai yang dihitung dengan cara variabel rentang sebelumnya. Setiap klausa where adalah filter yang mengecualikan item dari hasilnya. Setiap klausa join membandingkan kunci tertentu dari urutan sumber dengan kunci urutan lain, menghasilkan pasangan yang cocok. Setiap klausa orderby menyusun ulang item sesuai dengan kriteria yang ditentukan. Klausa select akhir atau group menentukan bentuk hasil dalam hal variabel rentang. Terakhir, klausa into dapat digunakan untuk "splice" kueri dengan memperlakukan hasil satu kueri sebagai generator dalam kueri berikutnya.

12.22.2 Ambiguities dalam ekspresi kueri

Ekspresi kueri menggunakan sejumlah kata kunci kontekstual (6,4,4): ascending, by, descending, equals, from, group, into, join, let, on, orderby, select dan where.

Untuk menghindari ambiguitas yang dapat muncul dari penggunaan pengidentifikasi ini baik sebagai kata kunci maupun nama sederhana, pengidentifikasi ini dianggap sebagai kata kunci di mana saja dalam ekspresi kueri, kecuali diawali dengan "@" (§6.4.4) dalam hal ini mereka dianggap pengidentifikasi. Untuk tujuan ini, ekspresi kueri adalah ekspresi apa pun yang dimulai dengan " pengidentifikasifrom" diikuti oleh token apa pun kecuali ";", "=" atau ",".

12.22.3 Terjemahan ekspresi kueri

12.22.3.1 Umum

Bahasa C# tidak menentukan semantik eksekusi ekspresi kueri. Sebaliknya, ekspresi kueri diterjemahkan ke dalam pemanggilan metode yang mematuhi pola ekspresi kueri (§12.22.4). Secara khusus, ekspresi kueri diterjemahkan ke dalam pemanggilan metode bernama Where, Select, SelectMany, Join, GroupJoin, OrderBy, OrderByDescending, ThenBy, ThenByDescending, GroupBy, dan Cast. Metode ini diharapkan memiliki tanda tangan tertentu dan jenis pengembalian, seperti yang dijelaskan dalam §12.22.4. Metode ini mungkin merupakan metode instans objek yang dikueri atau metode ekstensi yang berada di luar objek. Metode ini mengimplementasikan eksekusi kueri yang sebenarnya.

Terjemahan dari ekspresi kueri ke pemanggilan metode adalah pemetaan sintaktik yang terjadi sebelum pengikatan tipe atau penyelesaian kelebihan muatan dilakukan. Setelah terjemahan ekspresi kueri, pemanggilan metode yang dihasilkan diproses sebagai pemanggilan metode reguler, dan ini pada gilirannya dapat mengungkap kesalahan waktu kompilasi. Kondisi kesalahan ini termasuk, tetapi tidak terbatas pada, metode yang tidak ada, argumen dari jenis yang salah, dan metode generik di mana inferensi jenis gagal.

Ekspresi kueri diproses dengan berulang kali menerapkan terjemahan berikut sampai tidak ada pengurangan lebih lanjut yang dimungkinkan. Terjemahan tercantum dalam urutan penerapan: setiap bagian mengasumsikan bahwa terjemahan di bagian sebelumnya telah dilakukan secara menyeluruh, dan setelah tuntas, bagian tersebut tidak akan ditinjau kembali dalam pengolahan query yang sama.

Ini adalah kesalahan saat waktu kompilasi jika ekspresi kueri menyertakan penugasan ke variabel rentang, atau penggunaan variabel rentang sebagai argumen untuk parameter referensi atau keluaran.

Terjemahan tertentu menyisipkan variabel rentang dengan pengidentifikasi transparan yang ditandai dengan *. Ini dijelaskan lebih lanjut dalam §12.22.3.8.

12.22.3.2 Ekspresi kueri dengan kelanjutan

Ekspresi kueri dengan kelanjutan yang mengikuti isi kuerinya

from «x1» in «e1» «b1» into «x2» «b2»

diterjemahkan ke dalam

from «x2» in ( from «x1» in «e1» «b1» ) «b2»

Terjemahan di bagian berikut mengasumsikan bahwa kueri tidak memiliki kelanjutan.

Contoh: Contoh:

from c in customers
group c by c.Country into g
select new { Country = g.Key, CustCount = g.Count() }

diterjemahkan ke dalam:

from g in
   (from c in customers
   group c by c.Country)
select new { Country = g.Key, CustCount = g.Count() }

terjemahan akhirnya adalah:

customers.
GroupBy(c => c.Country).
Select(g => new { Country = g.Key, CustCount = g.Count() })

contoh akhir

12.22.3.3 Jenis variabel rentang eksplisit

Klausa from yang secara eksplisit menentukan jenis variabel rentang

from «T» «x» in «e»

diterjemahkan ke dalam

from «x» in ( «e» ) . Cast < «T» > ( )

Klausa join yang secara eksplisit menentukan jenis variabel rentang

join «T» «x» in «e» on «k1» equals «k2»

diterjemahkan ke dalam

join «x» in ( «e» ) . Cast < «T» > ( ) on «k1» equals «k2»

Terjemahan di bagian berikut mengasumsikan bahwa kueri tidak memiliki jenis variabel rentang eksplisit.

Contoh: Contoh

from Customer c in customers
where c.City == "London"
select c

diterjemahkan ke dalam

from c in (customers).Cast<Customer>()
where c.City == "London"
select c

terjemahan akhirnya adalah

customers.
Cast<Customer>().
Where(c => c.City == "London")

contoh akhir

Catatan: Jenis variabel rentang eksplisit berguna untuk mengkueri koleksi yang mengimplementasikan antarmuka IEnumerable non-generik, tetapi bukan antarmuka IEnumerable<T> generik. Dalam contoh di atas, ini akan terjadi jika pelanggan berjenis ArrayList. catatan akhir

12.22.3.4 Degenerasi ekspresi kueri

Ekspresi kueri dalam bentuk

from «x» in «e» select «x»

diterjemahkan ke dalam

( «e» ) . Select ( «x» => «x» )

Contoh: Contoh

from c in customers
select c

diterjemahkan ke dalam

(customers).Select(c => c)

contoh akhir

Ekspresi kueri degenerasi adalah ekspresi yang memilih elemen sumber secara sepele.

Catatan: Fase selanjutnya dari terjemahan (§12.22.3.6 dan §12.22.3.7) menghapus kueri degenerasi yang diperkenalkan oleh langkah-langkah terjemahan lainnya dengan menggantinya dengan sumbernya. Namun, penting untuk memastikan bahwa hasil ekspresi kueri tidak pernah menjadi objek sumber itu sendiri. Jika tidak, mengembalikan hasil kueri tersebut mungkin secara tidak sengaja mengekspos data privat (misalnya, array elemen) ke pemanggil. Oleh karena itu, langkah ini melindungi kueri degenerasi yang ditulis langsung dalam kode sumber dengan secara eksplisit memanggil Select pada sumbernya. Kemudian terserah pelaksana Select dan operator kueri lainnya untuk memastikan bahwa metode ini tidak pernah mengembalikan objek sumber itu sendiri. catatan akhir

12.22.3.5 Dari, biarkan, di mana, bergabung dan orderby klausa

Ekspresi kueri dengan klausa kedua from yang diikuti oleh klausa select

from «x1» in «e1»  
from «x2» in «e2»  
select «v»

diterjemahkan ke dalam

( «e1» ) . SelectMany( «x1» => «e2» , ( «x1» , «x2» ) => «v» )

Contoh: Contoh

from c in customers
from o in c.Orders
select new { c.Name, o.OrderID, o.Total }

diterjemahkan ke dalam

(customers).
SelectMany(c => c.Orders,
(c,o) => new { c.Name, o.OrderID, o.Total }
)

contoh akhir

Ekspresi kueri dengan klausa from kedua diikuti oleh isi kueri Q berisi sekumpulan klausa isi kueri yang tidak kosong:

from «x1» in «e1»
from «x2» in «e2»
Q

diterjemahkan ke dalam

from * in («e1») . SelectMany( «x1» => «e2» ,
                              ( «x1» , «x2» ) => new { «x1» , «x2» } )
Q

Contoh: Contoh

from c in customers
from o in c.Orders
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total }

diterjemahkan ke dalam

from * in (customers).
   SelectMany(c => c.Orders, (c,o) => new { c, o })
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total }

terjemahan akhirnya adalah

customers.
SelectMany(c => c.Orders, (c,o) => new { c, o }).
OrderByDescending(x => x.o.Total).
Select(x => new { x.c.Name, x.o.OrderID, x.o.Total })

di mana x adalah pengidentifikasi yang dihasilkan kompilator yang tidak terlihat dan tidak dapat diakses.

contoh akhir

Ekspresi let bersama dengan klausa from sebelumnya:

from «x» in «e»  
let «y» = «f»  
...

diterjemahkan ke dalam

from * in ( «e» ) . Select ( «x» => new { «x» , «y» = «f» } )  
...

Contoh: Contoh

from o in orders
let t = o.Details.Sum(d => d.UnitPrice * d.Quantity)
where t >= 1000
select new { o.OrderID, Total = t }

diterjemahkan ke dalam

from * in (orders).Select(
    o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) })
where t >= 1000
select new { o.OrderID, Total = t }

terjemahan akhirnya adalah

orders
    .Select(o => new { o, t = o.Details.Sum(d => d.UnitPrice * d.Quantity) })
    .Where(x => x.t >= 1000)
    .Select(x => new { x.o.OrderID, Total = x.t })

di mana x adalah pengidentifikasi yang dihasilkan kompilator yang tidak terlihat dan tidak dapat diakses.

contoh akhir

Ekspresi where bersama dengan klausa from sebelumnya:

from «x» in «e»  
where «f»  
...

diterjemahkan ke dalam

from «x» in ( «e» ) . Where ( «x» => «f» )  
...

Klausa join segera diikuti oleh klausa select

from «x1» in «e1»  
join «x2» in «e2» on «k1» equals «k2»  
select «v»

diterjemahkan ke dalam

( «e1» ) . Join( «e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «x2» ) => «v» )

Contoh: Contoh

from c in customers
join o in orders on c.CustomerID equals o.CustomerID
select new { c.Name, o.OrderDate, o.Total }

diterjemahkan ke dalam

(customers).Join(
   orders,
   c => c.CustomerID, o => o.CustomerID,
   (c, o) => new { c.Name, o.OrderDate, o.Total })

contoh akhir

Klausa join diikuti oleh klausa isi kueri:

from «x1» in «e1»  
join «x2» in «e2» on «k1» equals «k2»  
...

diterjemahkan ke dalam

from * in ( «e1» ) . Join(  
«e2» , «x1» => «k1» , «x2» => «k2» ,
( «x1» , «x2» ) => new { «x1» , «x2» })  
...

Klausa join-into segera diikuti oleh klausa select

from «x1» in «e1»  
join «x2» in «e2» on «k1» equals «k2» into «g»  
select «v»

diterjemahkan ke dalam

( «e1» ) . GroupJoin( «e2» , «x1» => «k1» , «x2» => «k2» ,
                     ( «x1» , «g» ) => «v» )

Klausa join into diikuti oleh klausa isi kueri

from «x1» in «e1»  
join «x2» in «e2» on «k1» equals «k2» into *g»  
...

diterjemahkan ke dalam

from * in ( «e1» ) . GroupJoin(  
   «e2» , «x1» => «k1» , «x2» => «k2» , ( «x1» , «g» ) => new { «x1» , «g» })
...

Contoh: Contoh

from c in customers
join o in orders on c.CustomerID equals o.CustomerID into co
let n = co.Count()
where n >= 10
select new { c.Name, OrderCount = n }

diterjemahkan ke dalam

from * in (customers).GroupJoin(
    orders,
    c => c.CustomerID,
    o => o.CustomerID,
    (c, co) => new { c, co })
let n = co.Count()
where n >= 10
select new { c.Name, OrderCount = n }

terjemahan akhirnya adalah

customers
    .GroupJoin(
        orders,
        c => c.CustomerID,
        o => o.CustomerID,
        (c, co) => new { c, co })
    .Select(x => new { x, n = x.co.Count() })
    .Where(y => y.n >= 10)
    .Select(y => new { y.x.c.Name, OrderCount = y.n })

di mana x dan y adalah pengidentifikasi yang dihasilkan kompilator yang tidak terlihat dan tidak dapat diakses.

contoh akhir

Klausa orderby dan klausul from sebelumnya:

from «x» in «e»  
orderby «k1» , «k2» , ... , «kn»  
...

diterjemahkan ke dalam

from «x» in ( «e» ) .
OrderBy ( «x» => «k1» ) .
ThenBy ( «x» => «k2» ) .
... .
ThenBy ( «x» => «kn» )
...

Jika klausul ordering menentukan indikator arah menurun, pemanggilan OrderByDescending atau ThenByDescending diproduksi sebagai gantinya.

Contoh: Contoh

from o in orders
orderby o.Customer.Name, o.Total descending
select o

memiliki terjemahan akhir

(orders)
    .OrderBy(o => o.Customer.Name)
    .ThenByDescending(o => o.Total)

contoh akhir

Terjemahan berikut mengasumsikan bahwa tidak ada klausa let, where, join atau orderby, dan tidak lebih dari satu klausa from awal di setiap ekspresi kueri.

12.22.3.6 Pilih klausa

Ekspresi kueri dalam bentuk

from «x» in «e» select «v»

diterjemahkan ke dalam

( «e» ) . Select ( «x» => «v» )

kecuali ketika «v» adalah pengidentifikasi «x», terjemahannya sederhana

( «e» )

Contoh: Contoh

from c in customers.Where(c => c.City == "London")
select c

hanya diterjemahkan ke dalam

(customers).Where(c => c.City == "London")

contoh akhir

12.22.3.7 Klausa grup

Klausa group

from «x» in «e» group «v» by «k»

diterjemahkan ke dalam

( «e» ) . GroupBy ( «x» => «k» , «x» => «v» )

kecuali ketika «v» adalah pengidentifikasi «x», terjemahannya adalah

( «e» ) . GroupBy ( «x» => «k» )

Contoh: Contoh

from c in customers
group c.Name by c.Country

diterjemahkan ke dalam

(customers).GroupBy(c => c.Country, c => c.Name)

contoh akhir

12.22.3.8 Pengidentifikasi transparan

Terjemahan tertentu menyuntikkan variabel rentang dengan pengidentifikasi transparanyang ditandai oleh *. Pengidentifikasi transparan hanya ada sebagai langkah perantara dalam proses terjemahan ekspresi kueri.

Saat terjemahan kueri menyuntikkan pengidentifikasi transparan, langkah-langkah terjemahan lebih lanjut menyebarluaskan pengidentifikasi transparan ke dalam fungsi anonim dan penginisialisasi objek anonim. Dalam konteks tersebut, pengidentifikasi transparan memiliki perilaku berikut:

  • Ketika pengidentifikasi transparan terjadi sebagai parameter dalam fungsi anonim, anggota jenis anonim terkait secara otomatis berada dalam cakupan dalam isi fungsi anonim.
  • Ketika sebuah anggota dengan pengidentifikasi transparan ada dalam cakupan, maka anggota-anggota dari anggota tersebut juga ada dalam cakupan.
  • Ketika sebuah pengidentifikasi transparan muncul sebagai deklarator anggota dalam penginisialisasi objek anonim, hal ini memperkenalkan anggota dengan pengidentifikasi yang transparan.

Dalam langkah-langkah terjemahan yang dijelaskan di atas, pengidentifikasi transparan selalu diperkenalkan bersama dengan jenis anonim, dengan tujuan menangkap beberapa variabel rentang sebagai anggota satu objek. Implementasi C# diizinkan untuk menggunakan mekanisme yang berbeda dari tipe anonim untuk mengelompokkan beberapa variabel rentang. Contoh terjemahan berikut mengasumsikan bahwa jenis anonim digunakan, dan menunjukkan satu kemungkinan terjemahan pengidentifikasi transparan.

Contoh: Contoh

from c in customers
from o in c.Orders
orderby o.Total descending
select new { c.Name, o.Total }

diterjemahkan ke dalam

from * in (customers).SelectMany(c => c.Orders, (c,o) => new { c, o })
orderby o.Total descending
select new { c.Name, o.Total }

yang selanjutnya diterjemahkan ke dalam

customers
    .SelectMany(c => c.Orders, (c,o) => new { c, o })
    .OrderByDescending(* => o.Total)
    .Select(\* => new { c.Name, o.Total })

yang, ketika pengenal transparan dihapus, setara dengan

customers
    .SelectMany(c => c.Orders, (c,o) => new { c, o })
    .OrderByDescending(x => x.o.Total)
    .Select(x => new { x.c.Name, x.o.Total })

di mana x adalah pengidentifikasi yang dihasilkan kompilator yang tidak terlihat dan tidak dapat diakses.

Contoh

from c in customers
join o in orders on c.CustomerID equals o.CustomerID
join d in details on o.OrderID equals d.OrderID
join p in products on d.ProductID equals p.ProductID
select new { c.Name, o.OrderDate, p.ProductName }

diterjemahkan ke dalam

from * in (customers).Join(
    orders,
    c => c.CustomerID,
    o => o.CustomerID,
    (c, o) => new { c, o })
join d in details on o.OrderID equals d.OrderID
join p in products on d.ProductID equals p.ProductID
select new { c.Name, o.OrderDate, p.ProductName }

yang selanjutnya dikurangi menjadi

customers
    .Join(orders, c => c.CustomerID,
        o => o.CustomerID, (c, o) => new { c, o })
    .Join(details, * => o.OrderID, d => d.OrderID, (*, d) => new { *, d })
    .Join(products, * => d.ProductID, p => p.ProductID,
        (*, p) => new { c.Name, o.OrderDate, p.ProductName })

terjemahan akhirnya adalah

customers
    .Join(orders, c => c.CustomerID,
        o => o.CustomerID, (c, o) => new { c, o })
    .Join(details, x => x.o.OrderID, d => d.OrderID, (x, d) => new { x, d })
    .Join(products, y => y.d.ProductID, p => p.ProductID,
        (y, p) => new { y.x.c.Name, y.x.o.OrderDate, p.ProductName })

di mana x dan y adalah pengidentifikasi yang dihasilkan kompilator yang tidak terlihat dan tidak dapat diakses. contoh akhir

12.22.4 Pola ekspresi kueri

Pola Ekspresi kueri menetapkan pola metode yang bisa diterapkan tipe untuk mendukung ekspresi kueri.

Jenis generik C<T> mendukung pola ekspresi kueri jika metode anggota publiknya dan metode ekstensi yang dapat diakses publik dapat digantikan oleh definisi kelas berikut. Anggota dan metode ekstensi yang dapat diakses disebut sebagai "bentuk" dari jenis C<T>generik . Jenis generik digunakan untuk mengilustrasikan hubungan yang tepat antara parameter dan jenis pengembalian, tetapi dimungkinkan untuk mengimplementasikan pola untuk jenis non-generik juga.

delegate R Func<T1,R>(T1 arg1);
delegate R Func<T1,T2,R>(T1 arg1, T2 arg2);

class C
{
    public C<T> Cast<T>() { ... }
}

class C<T> : C
{
    public C<T> Where(Func<T,bool> predicate) { ... }
    public C<U> Select<U>(Func<T,U> selector) { ... }
    public C<V> SelectMany<U,V>(Func<T,C<U>> selector,
        Func<T,U,V> resultSelector) { ... }
    public C<V> Join<U,K,V>(C<U> inner, Func<T,K> outerKeySelector,
        Func<U,K> innerKeySelector, Func<T,U,V> resultSelector) { ... }
    public C<V> GroupJoin<U,K,V>(C<U> inner, Func<T,K> outerKeySelector,
        Func<U,K> innerKeySelector, Func<T,C<U>,V> resultSelector) { ... }
    public O<T> OrderBy<K>(Func<T,K> keySelector) { ... }
    public O<T> OrderByDescending<K>(Func<T,K> keySelector) { ... }
    public C<G<K,T>> GroupBy<K>(Func<T,K> keySelector) { ... }
    public C<G<K,E>> GroupBy<K,E>(Func<T,K> keySelector,
        Func<T,E> elementSelector) { ... }
}

class O<T> : C<T>
{
    public O<T> ThenBy<K>(Func<T,K> keySelector) { ... }
    public O<T> ThenByDescending<K>(Func<T,K> keySelector) { ... }
}

class G<K,T> : C<T>
{
    public K Key { get; }
}

Metode di atas menggunakan jenis delegasi generik Func<T1, R> dan Func<T1, T2, R>, tetapi sama-sama dapat menggunakan jenis delegasi atau pohon ekspresi lainnya dengan hubungan yang sama dalam parameter dan jenis pengembalian.

Catatan: Hubungan yang direkomendasikan antara C<T> dan O<T> yang memastikan bahwa metode ThenBy dan ThenByDescending hanya tersedia pada hasil OrderBy atau OrderByDescending. catatan akhir

Catatan: Bentuk hasil yang direkomendasikan dari GroupBy—serangkaian urutan, di mana setiap urutan memiliki properti Key tambahan. catatan akhir

Catatan: Karena ekspresi kueri diterjemahkan ke pemanggilan metode dengan cara pemetaan sintaksis, jenis memiliki fleksibilitas yang cukup besar dalam cara mereka menerapkan salah satu atau semua pola ekspresi kueri. Misalnya, metode pola dapat diimplementasikan sebagai metode instans atau sebagai metode ekstensi karena keduanya memiliki sintaks pemanggilan yang sama, dan metode dapat meminta delegasi atau pohon ekspresi karena fungsi anonim dapat dikonversi ke keduanya. Jenis yang hanya menerapkan sebagian pola ekspresi kueri hanya mendukung terjemahan ekpresi kueri yang sesuai dengan metode yang didukung oleh jenis tersebut. catatan akhir

Note: Namespace System.Linq menyediakan implementasi pola ekspresi kueri untuk jenis apa pun yang mengimplementasikan antarmuka System.Collections.Generic.IEnumerable<T>. catatan akhir

12.23 Operator penugasan

12.23.1 Umum

Semua kecuali salah satu operator penugasan menetapkan nilai baru ke variabel, properti, peristiwa, atau elemen pengindeks. Pengecualian, = ref, menetapkan referensi variabel (§9,5) ke variabel referensi (§9,7).

assignment
    : unary_expression assignment_operator expression
    ;

assignment_operator
    : '=' 'ref'? | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
      '<<=' | '??='
    | right_shift_assignment
    ;

Kecuali untuk = ref, operan kiri penugasan harus berupa ekspresi yang diklasifikasikan sebagai variabel, atau akses untuk properti, akses untuk pengindeks, akses untuk peristiwa, atau tuple. Ekspresi deklarasi tidak diizinkan secara langsung sebagai sebuah operand kiri, tetapi dapat terjadi sebagai langkah dalam evaluasi penugasan dekontruksi.

Operator = disebut operator penugasan sederhana . Ini menetapkan nilai atau nilai operand kanan ke variabel, properti, elemen pengindeks atau elemen tuple yang diberikan oleh operand kiri. Operan kiri operator penugasan sederhana tidak boleh menjadi akses peristiwa (kecuali seperti yang dijelaskan dalam §15.8.2). Operator penugasan sederhana dijelaskan dalam §12.23.2.

Operator = ref disebut operator penugasan ref . Ini menjadikan operan kanan, yang harus berupa variable_reference (§9.5), sebagai acuan dari variabel yang dirujuk oleh operan kiri. Operator penugasan ref dijelaskan dalam §12.23.3.

Operator penugasan selain = operator dan = ref disebut operator penugasan gabungan. Operator ini diproses sebagai berikut:

  • Untuk operator ??=, hanya jika nilai operand kiri adalah null, operand kanan dievaluasi dan hasilnya ditetapkan ke variabel, properti, atau elemen pengindeks yang diberikan oleh operand kiri.
  • Jika tidak, operasi yang ditunjukkan dilakukan pada dua operand, dan kemudian nilai yang dihasilkan ditetapkan ke variabel, properti, atau elemen pengindeks yang diberikan oleh operand kiri. Operator penetapan gabungan dijelaskan dalam §12.23.4.

Operator += dan -= dengan ekspresi akses peristiwa sebagai operand kiri disebut operator penetapan peristiwa. Tidak ada operator penugasan lain yang valid dengan akses peristiwa sebagai operand kiri. Operator penetapan peristiwa dijelaskan dalam §12.23.5.

Operator penugasan bersifat asosiatif kanan, yang berarti bahwa operasi dikelompokkan dari kanan ke kiri.

Contoh: Ekspresi formulir a = b = c dievaluasi sebagai a = (b = c). contoh akhir

12.23.2 Penugasan sederhana

Operator = disebut operator penugasan sederhana.

Jika operan kiri dari tugas sederhana adalah formulir E.P atau E[Ei] di mana E memiliki jenis waktu kompilasi dynamic, maka penugasan terikat secara dinamis (§12.3.3). Dalam kasus ini, tipe kompilasi waktu dari ekspresi penugasan adalah dynamic, dan resolusi yang akan dijelaskan di bawah ini akan terjadi pada saat run-time berdasarkan tipe run-time dari E. Jika operand kiri adalah bentuk E[Ei] di mana setidaknya satu elemen Ei memiliki jenis waktu kompilasi dynamic, dan jenis waktu kompilasi E bukan array, akses pengindeks yang dihasilkan terikat secara dinamis, tetapi dengan pemeriksaan waktu kompilasi terbatas (§12.6.5).

Penugasan sederhana di mana operand kiri diklasifikasikan sebagai tuple juga disebut penugasan dekonstruktif. Jika salah satu elemen tuple dari operand kiri memiliki nama elemen, akan terjadi kesalahan saat kompilasi. Jika salah satu elemen tuple dari operand kiri adalah declaration_expression dan elemen lainnya bukan declaration_expression atau pembuangan sederhana, akan terjadi kesalahan pada waktu kompilasi.

Jenis x = y penetapan sederhana adalah tipe penetapan ke x dari y, yang ditentukan secara rekursif sebagai berikut:

  • Jika x adalah ekspresi tuple (x1, ..., xn), dan y dapat didekonstruksi ke ekspresi tuple (y1, ..., yn) dengan elemen n (§12,7), dan setiap penugasan untuk xiyi memiliki jenis Ti, maka tugas memiliki jenis (T1, ..., Tn).
  • Jika tidak, jika x diklasifikasikan sebagai variabel, variabel tidak readonly, x memiliki jenis T, dan y memiliki konversi implisit ke T, maka pengisian nilai memiliki jenis T.
  • Jika tidak, jika x diklasifikasikan sebagai variabel yang diketik secara implisit (yaitu ekspresi deklarasi yang diketik secara implisit) dan y memiliki jenis T, maka jenis variabel yang disimpulkan T, dan penugasan memiliki jenis T.
  • Selain itu, jika x diklasifikasikan sebagai akses properti atau pengindeks, dan properti atau pengindeks tersebut memiliki aksesor set yang dapat diakses, x memiliki jenis T, serta y memiliki konversi implisit ke T, maka pengisian tersebut memiliki jenis T.
  • Jika tidak, penugasan tidak valid dan terjadi kesalahan waktu pengikatan.

Pemrosesan run-time dari penugasan sederhana x = y dengan jenis T dilakukan sebagai penugasan ke x dari y dengan jenis T, yang terdiri dari langkah-langkah rekursif berikut:

  • x dievaluasi jika belum dievaluasi sebelumnya.
  • Jika x diklasifikasikan sebagai variabel, y dievaluasi dan, jika diperlukan, dikonversi ke T melalui konversi implisit (§10,2).
    • Jika variabel yang diberikan oleh x adalah elemen array dari reference_type, pemeriksaan waktu jalan dilakukan untuk memastikan bahwa nilai yang dihitung untuk y kompatibel dengan instance array tersebut yang merupakan elemen x. Pemeriksaan berhasil jika ynull, atau jika konversi referensi implisit (§10,2,8) ada dari jenis instans yang dirujuk oleh y ke jenis elemen aktual dari instans array yang berisi x. Jika tidak, System.ArrayTypeMismatchException akan dilempar.
    • Nilai yang dihasilkan dari evaluasi dan konversi y disimpan ke lokasi yang diberikan oleh evaluasi x, dan dihasilkan sebagai hasil dari pengalokasian.
  • Jika x diklasifikasikan sebagai akses properti atau pengindeks:
    • y dievaluasi dan, jika diperlukan, dikonversi ke T melalui konversi implisit (§10,2).
    • Aksesor set x dipanggil dengan nilai hasil evaluasi dan konversi dari y sebagai argumen nilainya.
    • Nilai yang dihasilkan dari evaluasi dan konversi y dihasilkan sebagai hasil dari penugasan.
  • Jika x diklasifikasikan sebagai tuple (x1, ..., xn) dengan aritas n:
    • y didekonstruksi menggunakan elemen n menjadi ekspresi tuple e.
    • t tuple hasil dibuat dengan mengonversi e menjadi T melalui konversi tuple implisit.
    • untuk setiap xi secara berurutan dari kiri ke kanan, penugasan ke xi dari t.Itemi dilakukan, kecuali bahwa xi tidak dievaluasi lagi.
    • t dihasilkan sebagai hasil dari penugasan.

Catatan: jika jenis waktu kompilasi x adalah dynamic dan ada konversi implisit dari jenis waktu kompilasi y ke dynamic, tidak diperlukan resolusi runtime. catatan akhir

Catatan: Aturan kovarians array (§17.6) mengizinkan nilai tipe array A[] sebagai referensi ke sebuah instans tipe array B[], dengan syarat terdapat konversi referensi implisit dari B ke A. Karena aturan ini, pengisian ke elemen array reference_type memerlukan pemeriksaan saat runtime untuk memastikan bahwa nilai yang diberikan kompatibel dengan instance array. Dalam contoh

string[] sa = new string[10];
object[] oa = sa;
oa[0] = null;              // OK
oa[1] = "Hello";           // OK
oa[2] = new ArrayList();   // ArrayTypeMismatchException

penugasan terakhir menyebabkan System.ArrayTypeMismatchException dilemparkan karena referensi ke ArrayList tidak dapat disimpan dalam elemen string[].

catatan akhir

Ketika properti atau pengindeks yang dideklarasikan dalam struct_type adalah target penugasan, ekspresi instans yang terkait dengan akses properti atau pengindeks harus diklasifikasikan sebagai variabel. Jika ekspresi instans diklasifikasikan sebagai nilai, kesalahan saat pengikatan terjadi.

Catatan: Karena §12.8.7, aturan yang sama juga berlaku untuk bidang. catatan akhir

Contoh: Diberikan deklarasi:

struct Point
{
   int x, y;

   public Point(int x, int y)
   {
      this.x = x;
      this.y = y;
   }

   public int X
   {
      get { return x; }
      set { x = value; }
   }

   public int Y {
      get { return y; }
      set { y = value; }
   }
}

struct Rectangle
{
    Point a, b;

    public Rectangle(Point a, Point b)
    {
        this.a = a;
        this.b = b;
    }

    public Point A
    {
        get { return a; }
        set { a = value; }
    }

    public Point B
    {
        get { return b; }
        set { b = value; }
    }
}

dalam contoh

Point p = new Point();
p.X = 100;
p.Y = 100;
Rectangle r = new Rectangle();
r.A = new Point(10, 10);
r.B = p;

tugas untuk p.X, p.Y, r.A, dan r.B diizinkan karena p dan r adalah variabel. Namun, dalam contoh

Rectangle r = new Rectangle();
r.A.X = 10;
r.A.Y = 10;
r.B.X = 100;
r.B.Y = 100;

semua tugas tidak valid, karena r.A dan r.B bukan variabel.

contoh akhir

12.23.3 Penetapan ref

Operator = ref dikenal sebagai operator penetapan ref .

Operan kiri harus berupa ekspresi yang mengikat ke variabel referensi (§9,7), parameter referensi (selain this), parameter output, atau parameter input. Operand kanan akan menjadi ekspresi yang menghasilkan variable_reference menunjuk nilai dengan jenis yang sama dengan operand kiri.

Ini adalah kesalahan waktu kompilasi jika ref-safe-context (§9.7.2) dari operand kiri lebih luas daripada ref-safe-context operand kanan.

Operan yang tepat pasti akan ditetapkan pada titik penugasan ref.

Ketika operand kiri terikat pada parameter output, kesalahan terjadi jika parameter output tersebut belum pasti ditetapkan pada awal penggunaan operator penugasan ref.

Jika operan kiri adalah ref yang dapat ditulis (yaitu, menunjuk selain parameter lokal atau input ref readonly), maka operan kanan harus merupakan variable_reference yang dapat ditulis. Jika variabel operand kanan dapat ditulis, operand kiri mungkin berupa ref yang dapat ditulis atau baca-saja.

Operasi ini membuat operan kiri menjadi alias dari variabel operand kanan. Alias dapat dibuat baca-saja meskipun variabel operan kanan dapat ditulis.

Operator penugasan ref menghasilkan variable_reference dengan jenis yang ditentukan. Ini dapat ditulis ulang jika operand kiri dapat ditulis ulang.

Operator penugasan ref tidak boleh membaca lokasi penyimpanan yang direferensikan oleh operand kanan.

Contoh: Berikut adalah beberapa contoh penggunaan = ref:

public static int M1() { ... }
public static ref int M2() { ... }
public static ref uint M2u() { ... }
public static ref readonly int M3() { ... }
public static void Test()
{
int v = 42;
ref int r1 = ref v; // OK, r1 refers to v, which has value 42
r1 = ref M1();      // Error; M1 returns a value, not a reference
r1 = ref M2();      // OK; makes an alias
r1 = ref M2u();     // Error; lhs and rhs have different types
r1 = ref M3();    // error; M3 returns a ref readonly, which r1 cannot honor
ref readonly int r2 = ref v; // OK; make readonly alias to ref
r2 = ref M2();      // OK; makes an alias, adding read-only protection
r2 = ref M3();      // OK; makes an alias and honors the read-only
r2 = ref (r1 = ref M2());  // OK; r1 is an alias to a writable variable,
              // r2 is an alias (with read-only access) to the same variable
}

contoh akhir

Catatan: Saat membaca kode menggunakan operator = ref, mungkin menggoda untuk membaca bagian ref sebagai bagian dari operand. Ini sangat membingungkan ketika operand adalah ekspresi kondisional ?:. Misalnya, saat membaca ref int a = ref b ? ref x : ref y; penting untuk membaca ini karena = ref menjadi operator, dan b ? ref x : ref y menjadi operan yang tepat: ref int a = ref (b ? ref x : ref y);. Yang penting, ekspresi ref btidak bagian dari pernyataan itu, meskipun sekilas terlihat demikian. catatan akhir

12.23.4 Penetapan gabungan

Jika operan kiri dari penetapan majemuk berbentuk E.P atau E[Ei] di mana E memiliki tipe saat kompilasi dynamic, maka penetapan secara dinamis terkait (§12.3.3). Dalam kasus ini, tipe kompilasi waktu dari ekspresi penugasan adalah dynamic, dan resolusi yang akan dijelaskan di bawah ini akan terjadi pada saat run-time berdasarkan tipe run-time dari E. Jika operand kiri adalah bentuk E[Ei] di mana setidaknya satu elemen Ei memiliki jenis waktu kompilasi dynamic, dan jenis waktu kompilasi E bukan array, akses pengindeks yang dihasilkan terikat secara dinamis, tetapi dengan pemeriksaan waktu kompilasi terbatas (§12.6.5).

a ??= b setara dengan (T) (a ?? (a = b)), kecuali yang a hanya dievaluasi sekali, di mana T merupakan tipe a ketika tipe b bersifat dinamis, dan jika tidak, T merupakan tipe a ?? b.

Jika tidak, operasi formulir x «op»= y diproses dengan menerapkan resolusi kelebihan beban operator biner (§12.4.5) seolah-olah operasi ditulis x «op» y. Kemudian

  • Jika jenis pengembalian operator yang dipilih secara implisit dapat dikonversi ke jenis x, operasi dievaluasi sebagai x = x «op» y, kecuali bahwa x dievaluasi hanya sekali.
  • Jika tidak, jika operator yang dipilih adalah operator yang telah ditentukan sebelumnya, jika jenis pengembalian operator yang dipilih secara eksplisit dapat dikonversi ke jenis x , dan jika y secara implisit dapat dikonversi ke jenis x atau operator adalah operator shift, maka operasi dievaluasi sebagai x = (T)(x «op» y), di mana T adalah jenis x, kecuali bahwa x dievaluasi hanya sekali.
  • Jika tidak, penetapan majemuk tidak valid, dan terjadi kesalahan saat pengikatan.

Istilah "dievaluasi hanya sekali" berarti bahwa dalam evaluasi x «op» y, hasil dari semua ekspresi konstituen x disimpan sementara dan kemudian digunakan kembali saat melakukan penetapan ke x.

Contoh: Pada penugasan A()[B()] += C(), yang mana A adalah metode yang mengembalikan int[], dan B dan C adalah metode yang mengembalikan int, metode ini hanya dipanggil satu kali sesuai urutan A, B, C. contoh akhir

Ketika operand kiri dari penugasan gabungan adalah akses properti atau akses pengindeks, properti atau pengindeks harus memiliki aksesor get dan aksesor yang ditetapkan. Jika ini tidak terjadi, kesalahan waktu pengikatan terjadi.

Aturan kedua di atas mengizinkan x «op»= y dievaluasi sebagai x = (T)(x «op» y) dalam konteks tertentu. Aturan ada sehingga operator yang telah ditentukan sebelumnya dapat digunakan sebagai operator majemuk ketika operan kiri berjenis sbyte, byte, short, ushort, atau char. Bahkan ketika kedua argumen adalah salah satu jenis tersebut, operator yang telah ditentukan menghasilkan hasil dari jenis int, seperti yang dijelaskan dalam §12.4.7.3. Dengan demikian, tanpa cast, tidak mungkin menetapkan hasil pada operand kiri.

Efek intuitif dari aturan untuk operator yang telah ditentukan sebelumnya hanyalah bahwa x «op»= y diizinkan jika x «op» y dan x = y diizinkan.

Contoh: Dalam kode berikut

byte b = 0;
char ch = '\0';
int i = 0;
b += 1;           // OK
b += 1000;        // Error, b = 1000 not permitted
b += i;           // Error, b = i not permitted
b += (byte)i;     // OK
ch += 1;          // Error, ch = 1 not permitted
ch += (char)1;    // OK

alasan intuitif untuk setiap kesalahan adalah bahwa penugasan sederhana yang sesuai juga akan menjadi kesalahan.

contoh akhir

Catatan: Ini juga berarti bahwa operasi penugasan gabungan mendukung operator yang diangkat. Karena penugasan gabungan x «op»= y dievaluasi sebagai x = x «op» y atau x = (T)(x «op» y), aturan evaluasi secara implisit mencakup operator terangkat. catatan akhir

12.23.5 Penetapan peristiwa

Jika operan kiri operator a += or -= diklasifikasikan sebagai akses peristiwa, maka ekspresi dievaluasi sebagai berikut:

  • Ekspresi instans, jika ada, dari akses terhadap acara dievaluasi.
  • Operan kanan operator += atau -= dievaluasi, dan, jika diperlukan, dikonversi ke jenis operand kiri melalui konversi implisit (§10,2).
  • Aksesor dari peristiwa dipanggil, dengan daftar argumen yang terdiri dari nilai yang dihitung pada langkah sebelumnya. Jika operator +=, aksesnor add dipanggil; jika operator -=, aksesnor remove dipanggil.

Ekspresi penetapan peristiwa tidak menghasilkan nilai. Dengan demikian, ekspresi penetapan peristiwa hanya valid dalam konteks statement_expression (§13,7).

12.24 Ekspresi

Ekspresi adalah non_assignment_expression atau penugasan.

expression
    : non_assignment_expression
    | assignment
    ;

non_assignment_expression
    : declaration_expression
    | conditional_expression
    | lambda_expression
    | query_expression
    ;

12.25 Ekspresi konstanta

Ekspresi konstanta adalah ekspresi yang akan sepenuhnya dievaluasi pada waktu kompilasi.

constant_expression
    : expression
    ;

Ekspresi konstanta harus memiliki nilai null atau salah satu jenis berikut:

  • sbyte, , byte, shortushort, int, uint, longulong, , char, float, double, decimal, , bool, string;
  • jenis enumerasi; atau
  • ekspresi nilai default (§12.8.21) untuk jenis referensi.

Hanya konstruksi berikut yang diizinkan dalam ekspresi konstanta:

  • Literal (termasuk literal null).
  • Referensi ke const anggota kelas, struct, dan jenis antarmuka.
  • Referensi ke anggota jenis enumerasi.
  • Referensi ke konstanta lokal.
  • Subekspresi dalam kurung yang merupakan ekspresi tetap.
  • Ekspresi konversi tipe.
  • ekspresi checked dan unchecked.
  • nameof ekspresi.
  • Operator unary +, -, ! (negasi logis), dan ~ yang telah ditentukan sebelumnya.
  • Operator biner yang telah ditentukan sebelumnya +, -, *, /, %, <<, >>, &, |, ^, &&, ||, ==, !=, <, >, <=, dan >=.
  • Operator kondisi ?: tersebut.
  • Operator ! pengampunan null (§12,8,9).
  • sizeof ekspresi, asalkan jenis yang tidak dikelola adalah salah satu jenis yang ditentukan dalam §24.6.9 yang sizeof mengembalikan nilai konstanta.
  • Ekspresi nilai default, asalkan jenisnya adalah salah satu jenis yang tercantum di atas.

Konversi berikut diizinkan dalam ekspresi konstan:

  • Konversi identitas
  • Konversi numerik
  • Konversi enumerasi
  • Konversi ekspresi tetap
  • Konversi referensi implisit dan eksplisit, asalkan sumber konversi adalah ekspresi konstanta yang mengevaluasi ke nilai null.

Catatan: Konversi lain termasuk pembungkusan, pembongkaran, dan konversi referensi implisit dari sebuah nilai non-null tidak diizinkan dalam ekspresi konstanta. catatan akhir

Contoh: Dalam kode berikut

class C
{
    const object i = 5;         // error: boxing conversion not permitted
    const object str = "hello"; // error: implicit reference conversion
}

inisialisasi i adalah kesalahan karena konversi tinju diperlukan. Inisialisasi str dianggap sebagai kesalahan karena diperlukan konversi referensi implisit dari nilai yang bukannull.

contoh akhir

Setiap kali ekspresi memenuhi persyaratan yang tercantum di atas, ekspresi dievaluasi pada waktu kompilasi. Ini benar bahkan jika ekspresi adalah subekspresi ekspresi yang lebih besar yang berisi konstruksi non-konstan.

Evaluasi waktu kompilasi ekspresi konstan menggunakan aturan yang sama dengan evaluasi waktu run-time ekspresi non-konstan, kecuali di mana evaluasi waktu run-time akan melemparkan pengecualian, evaluasi waktu kompilasi menyebabkan kesalahan waktu kompilasi terjadi.

Kecuali ekspresi konstanta ditempatkan secara eksplisit dalam konteks unchecked, overflow yang terjadi dalam operasi dan konversi aritmatika tipe integral selama evaluasi ekspresi pada waktu kompilasi selalu menyebabkan kesalahan waktu kompilasi (§12.8.20).

Ekspresi konstanta diperlukan dalam konteks yang tercantum di bawah ini dan ini ditunjukkan dalam tata bahasa dengan menggunakan constant_expression. Dalam konteks ini, kesalahan waktu kompilasi terjadi jika ekspresi tidak dapat dievaluasi sepenuhnya pada waktu kompilasi.

  • Deklarasi konstanta (§15,4)
  • Deklarasi anggota enumerasi (§20.4)
  • Daftar parameter dengan argumen bawaan (§15.6.2)
  • case label dari pernyataan switch (§13.8.3).
  • pernyataan goto case (§13.10.4)
  • Panjang dimensi dalam ekspresi pembuatan array (§12.8.17.4) yang menyertakan penginisialisasi.
  • Atribut (§23)
  • Dalam constant_pattern (§11.2.3)

Konversi ekspresi konstanta implisit (§10.2.11) memungkinkan ekspresi konstan jenis int dikonversi ke sbyte, byte, short, ushort, uint, atau ulong, asalkan nilai ekspresi konstanta berada dalam rentang jenis tujuan.

12.26 Ekspresi Boolean

boolean_expression adalah ekspresi yang menghasilkan hasil dari jenis bool; baik secara langsung atau melalui penerapan operator true dalam konteks tertentu seperti yang ditentukan dalam hal berikut:

boolean_expression
    : expression
    ;

Ekspresi kondisional pengontrol dari if_statement (§13.8.2), while_statement (§13.9.2), do_statement (§13.9.3), atau for_statement (§13.9.4) adalah sebuah boolean_expression. Ekspresi ?: kondisional pengontrol operator (§12.20) mengikuti aturan yang sama dengan boolean_expression, tetapi karena alasan prioritas operator diklasifikasikan sebagai null_coalescing_expression.

Diperlukan boolean_expressionE agar dapat menghasilkan nilai dengan tipe bool, sebagai berikut:

  • Jika E secara implisit dapat dikonversi ke bool maka pada run-time konversi implisit diterapkan.
  • Jika tidak, resolusi kelebihan beban operator unary (§12.4.4) digunakan untuk menemukan implementasi terbaik unik operator true pada E, dan implementasi tersebut diterapkan pada waktu run-time.
  • Jika operator tersebut tidak ditemukan, kesalahan waktu pengikatan terjadi.