Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
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 olehthis(§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.WriteLinedipilih berdasarkan jenis waktu kompilasi argumen mereka. Dengan demikian, waktu pengikatan adalah waktu kompilasi.Panggilan ketiga terikat secara dinamis: kelebihan
Console.WriteLinedipilih 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
+,-,*,/, dannew. 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 (sepertix++). - 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), metodeFdipanggil menggunakan nilai lamai, maka metodeGdipanggil dengan nilai lamai, dan, akhirnya, metodeHdipanggil 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 * zdievaluasi sebagaix + (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.yx?.yf(x)a[x]a?[x]x++x--x!newtypeofdefaultcheckeduncheckeddelegatestackalloc§12,9 Unari +-!x~^++x--x(T)xawait x§12.10 Jangkauan ..§12.11 Beralih switch { … }§12.12 bersifat perkalian */%§12.12 Aditif +-§12.13 Pergeseran <<>>§12.14 Pengujian relasi dan tipe <><=>=isas§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 + zdievaluasi 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 = zdievaluasi sebagaix = (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..zdanx..(y..z)tidak valid seperti..non-asosiatif. contoh akhir
Prioritas dan asokiativitas dapat dikontrol menggunakan tanda kurung.
Contoh:
x + y * zpertama kali mengalikanydenganzlalu menambahkan hasilnya kex, tetapi(x + y) * zterlebih dahulu menambahkanxdanylalu mengalikan hasilnya denganz. 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
truedanfalsetidak 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
isatauas. 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 hasilboolyang 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
Xuntuk operasioperator «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
XdanYuntuk operasioperator «op»(x, y)telah ditentukan. Set ini terdiri dari penyatuan operator kandidat yang disediakan olehXdan operator kandidat yang disediakan olehY, masing-masing ditentukan menggunakan aturan §12.4.6. Untuk kumpulan gabungan, kandidat digabungkan sebagai berikut:- Jika
XdanYdapat dikonversi identitas, atau jikaXdanYberasal dari jenis dasar umum, operator kandidat bersama hanya terjadi dalam kumpulan gabungan sekali. - Jika ada konversi identitas antara
XdanY, operator«op»Ydisediakan olehYmemiliki jenis pengembalian yang sama dengan«op»Xyang disediakan olehXdan jenis operand«op»Ymemiliki konversi identitas ke jenis operand«op»Xyang sesuai maka hanya«op»Xyang terjadi dalam set.
- Jika
- 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₀. JikaTadalah jenis nilai nullable,T₀adalah jenis dasarnya; jika bukan,T₀sama denganT. - Untuk semua deklarasi
operator «op»dalamT₀dan semua bentuk operator yang diangkat, jika setidaknya satu operator berlaku (§12.6.4.2) sehubungan dengan daftar argumenA, maka kumpulan operator kandidat terdiri dari semua operator yang berlaku diT₀. - Jika tidak, apabila
T₀adalahobject, himpunan operator kandidat kosong. - Jika tidak, himpunan operator kandidat yang disediakan oleh
T₀adalah himpunan operator kandidat yang disediakan oleh kelas dasar langsungT₀, atau kelas dasar efektifT₀jikaT₀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 manabadalahbytedansadalahshort, resolusi kelebihan beban memilihoperator *(int, int)sebagai operator terbaik. Dengan demikian, efeknya adalah bahwabdansdikonversi keint, dan jenis hasilnyaint. Demikian juga, untuk operasii * d, di manaiadalahintdandadalah resolusidouble,overloadmemilihoperator *(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 jenisdecimal, atau kesalahan waktu pengikatan terjadi jika operand lain berjenisfloatataudouble. - Jika tidak, jika salah satu operan berjenis
double, operand lainnya dikonversi ke jenisdouble. - Jika tidak, jika salah satu operan berjenis
float, operand lainnya dikonversi ke jenisfloat. - Jika tidak, jika salah satu operan berjenis
ulong, operand lain dikonversi ke jenisulong, atau kesalahan waktu pengikatan terjadi jika operand laintype sbyte,short,int, ataulong. - Jika tidak, jika salah satu operan berjenis
long, operand lainnya dikonversi ke jenislong. - Jika tidak, jika salah satu operan berjenis
uintdan operand lainnya berjenissbyte,short, atauint, kedua operan dikonversi ke jenislong. - Jika tidak, jika salah satu operan berjenis
uint, operand lainnya dikonversi ke jenisuint. - Jika tidak, kedua operan dikonversi ke jenis
int.
Catatan: Aturan pertama melarang operasi apa pun yang mencampur jenis
decimaldengan jenisdoubledanfloat. Aturan ini mengikuti dari fakta bahwa tidak ada konversi implisit antara jenisdecimaldan jenisdoubledanfloat. catatan akhir
Catatan: Perhatikan juga bahwa tidak mungkin bagi operand memiliki tipe
ulongketika operand lainnya memiliki tipe integral bertanda. Alasannya adalah bahwa tidak ada jenis integral yang dapat mewakili rentang lengkapulongserta 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
decimaltidak dapat dikalikan dengandouble. Kesalahan diatasi dengan secara eksplisit mengonversi operand kedua kedecimal, 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 nilainulljika operand adalahnull. 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 menghasilkannullnilai jika satu atau kedua operan (pengecualian adalahnull&operator dan|jenisnyabool?, 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 adalahbool. Formulir yang ditingkatkan dibangun dengan menambahkan pengubah?tunggal ke setiap jenis operand. Operator yang diangkat menganggap dua nilainullsama, dan nilainulltidak sama dengan nilai non-nullapa pun. Jika kedua operan tidaknull, operator yang dinaikkan membuka bungkus operan dan menerapkan operator dasar untuk menghasilkan hasilbool. - Untuk operator relasional
<,>,<=, dan>=, bentuk operator yang diangkat ada jika jenis operand adalah jenis nilai yang tidak dapat diubah ke null dan jika jenis hasilnyabool. Formulir yang ditingkatkan dibangun dengan menambahkan pengubah?tunggal ke setiap jenis operand. Operator yang ditingkatkan menghasilkan nilaifalsejika satu atau kedua operan bernilainull. Jika tidak, operator yang diangkat membuka operand dan menerapkan operator dasar untuk menghasilkan hasilbool.
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
Nditentukan:- Jika
Tadalah parameter jenis, maka set adalah penyatuan kumpulan anggota yang dapat diakses bernamaNdi setiap jenis yang ditentukan sebagai batasan utama atau batasan sekunder (§15,2,5) untukT, bersama dengan set anggota yang dapat diakses bernamaNdiobject. - Jika tidak, set terdiri dari semua anggota yang dapat diakses (§7,5) yang bernama
NdiT, termasuk anggota yang diwariskan dan anggota yang dapat diakses bernamaNdiobject. JikaTadalah jenis yang dibangun, kumpulan anggota diperoleh dengan mengganti argumen jenis seperti yang dijelaskan dalam §15.3.3. Anggota yang menyertakan pengubahoverridedikecualikan dari set.
- Jika
- Selanjutnya, jika
Knol, semua jenis berlapis yang deklarasinya menyertakan parameter jenis akan dihapus. JikaKbukan nol, semua anggota dengan jumlah parameter jenis yang berbeda akan dihapus. KetikaKnol, 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.Mdalam set, di manaSadalah jenis di mana anggotaMdideklarasikan, aturan berikut diterapkan:- Jika
Madalah anggota konstanta, bidang, properti, peristiwa, atau enumerasi, maka semua anggota yang dideklarasikan dalam jenis dasarSdihapus dari set. - Jika
Madalah deklarasi jenis, maka semua non-jenis yang dideklarasikan dalam jenis dasarSdihapus dari set, dan semua deklarasi jenis dengan jumlah parameter jenis yang sama sepertiMdideklarasikan dalam jenis dasarSdihapus dari set. - Jika
Madalah metode, maka semua anggota non-metode yang dideklarasikan dalam jenis dasarSdihapus dari set.
- Jika
- Selanjutnya, anggota antarmuka yang disembunyikan oleh anggota kelas dihapus dari set. Langkah ini hanya memiliki efek jika
Tadalah parameter jenis danTmemiliki kelas dasar yang efektif selainobjectdan set antarmuka efektif yang tidak kosong (§15.2.5). Untuk setiap anggotaS.Mdalam set, di manaSadalah jenis di mana anggotaMdinyatakan, aturan berikut diterapkan jikaSadalah deklarasi kelas selainobject:- Jika
Madalah konstanta, bidang, properti, peristiwa, anggota enumerasi, atau deklarasi jenis, maka semua anggota yang dinyatakan dalam deklarasi antarmuka dihapus dari set. - Jika
Madalah metode, maka semua anggota non-metode yang dinyatakan dalam deklarasi antarmuka dihapus dari set, dan semua metode dengan tanda tangan yang sama sepertiMyang dinyatakan dalam deklarasi antarmuka dihapus dari set.
- Jika
- 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
Tobjectataudynamic, makaTtidak memiliki jenis dasar. - Jika
Tadalah enum_type, jenis dasarTadalah jenis kelasSystem.Enum,System.ValueType, danobject. - Jika
Tadalah struct_type, jenis dasarTadalah jenis kelasSystem.ValueTypedanobject.Catatan: nullable_value_type adalah struct_type (§8.3.1). catatan akhir
- Jika
Tadalah jenis_kelas, tipe dasar dariTadalah kelas induk dariT, termasuk tipe kelasobject. - Jika
Tadalah interface_type, tipe dasar dariTadalah antarmuka dasar dariTdan tipe dari kelasobject. - Jika
Tadalah array_type, jenis dasarTadalah jenis kelasSystem.Arraydanobject. - Jika
Tadalah delegate_type, jenis dasarTadalah jenis kelasSystem.Delegatedanobject.
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, danvaluemenunjukkan ekspresi yang diklasifikasikan sebagai variabel atau nilai,Tmenunjukkan ekspresi yang diklasifikasikan sebagai jenis,Fadalah nama sederhana metode, danPadalah nama sederhana properti.
Membangun Contoh Deskripsi Pemanggilan metode F(x, y)Resolusi kelebihan beban diterapkan untuk memilih metode terbaik Fdi kelas atau struktur yang berisi. Metode ini dipanggil dengan daftar argumen(x, y). Jika metodenya bukanstatic, maka ekspresi instans adalahthis.T.F(x, y)Resolusi kelebihan beban diterapkan untuk memilih metode terbaik Fdi kelas atau strukturT. Kesalahan waktu pengikatan terjadi jika metode tidakstatic. Metode ini dipanggil dengan daftar argumen(x, y).e.F(x, y)Penyelesaian overload diterapkan untuk memilih metode terbaik Fdalam kelas, struktur, atau antarmuka yang disebutkan oleh tipee. Kesalahan waktu pengikatan (binding-time error) terjadi jika metodestatic. Metode ini dipanggil dengan ekspresi instansedan daftar argumen(x, y).Akses properti PAkses pengambil nilai properti Pdalam kelas atau struktur tersebut dipanggil. Kesalahan waktu kompilasi terjadi jikaPhanya dapat ditulis. JikaPbukanstatic, ekspresi contoh adalahthis.P = valueAksesor set properti Pdi kelas yang berisi atau struktur dipanggil dengan daftar argumen(value). Kesalahan waktu kompilasi terjadi jikaPbersifat baca-saja. JikaPbukanstatic, ekspresi contoh adalahthis.T.PAkses get dari properti Pdi kelas atau structTdipanggil. Kesalahan waktu kompilasi terjadi jikaPbukanstaticatau jikaPhanya dapat ditulis.T.P = valueAksesor set properti Pdi kelas atau strukturTdipanggil dengan daftar argumen(value). Kesalahan waktu kompilasi terjadi jikaPtidakstaticatau jikaPbersifat baca-saja.e.PAksesor get dari properti Pdalam kelas, struct, atau antarmuka yang ditentukan oleh tipeE, dipanggil menggunakan ekspresi instancee. Kesalahan waktu pengikatan terjadi jikaPadalahstaticatau jikaPhanya dapat ditulis.e.P = valueAksesor set untuk properti Pdi dalam kelas, struct, atau antarmuka yang tipe-nya ditentukan olehEdipanggil dengan ekspresi instansedan daftar argumen(value). Kesalahan waktu pengikatan terjadi jikaPstaticatau jikaPbersifat baca-saja.Akses acara E += valueAkses tambahkan untuk peristiwa Edi kelas atau struktur yang berisi dipanggil. JikaEbukanstatic, ekspresi contoh adalahthis.E -= valueAksesor hapus dari acara Edi kelas atau struktur tempat acara itu berada dipanggil. JikaEbukanstatic, ekspresi contoh adalahthis.T.E += valueAkses tambah untuk peristiwa Edi kelas atau strukturTdipanggil. Kesalahan waktu pengikatan terjadi jikaEtidakstatic.T.E -= valueAksesor hapus untuk peristiwa Edi dalam kelas atau strukturTtelah dipanggil. Kesalahan waktu pengikatan terjadi jikaEtidakstatic.e.E += valueAksesor penambah dari peristiwa Edi dalam kelas, struct, atau antarmuka yang diberikan oleh jenisEdipanggil dengan menggunakan ekspresi instanse. Kesalahan waktu pengikatan terjadi jikaEadalahstatic.e.E -= valueAkses fungsi penghapusan dari event Edi dalam kelas, struct, atau antarmuka yang ditentukan oleh tipeEdipanggil menggunakan ekspresi instancee. Kesalahan waktu pengikatan terjadi jikaEadalahstatic.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 instanceedan daftar argumen(x, y). Kesalahan waktu pengikatan terjadi jika pengindeks hanya bisa menulis.e[x, y] = valueResolusi kelebihan beban diterapkan untuk memilih pengindeks terbaik di kelas, struct, atau antarmuka yang diberikan oleh jenis e. Aksesor set pengindeks dipanggil dengan ekspresi instansedan daftar argumen(x, y, value). Kesalahan waktu pengikatan terjadi jika pengindeks bersifat baca-saja.Pemanggilan operator -xResolusi 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 + yResolusi kelebihan beban diterapkan untuk memilih operator biner terbaik di kelas atau struktur yang diberikan oleh jenis xdany. 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
indiikuti 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
refdiikuti 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
outdiikuti 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
valuedari 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 olehM(c: false, valueB);. Argumen pertama digunakan di luar posisi (argumen digunakan dalam posisi pertama, tetapi parameter bernamacberada 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 argumentDalam panggilan metode
M1(i),iitu sendiri diteruskan sebagai argumen input, karena diklasifikasikan sebagai variabel dan memiliki jenisintyang sama dengan parameter input. Dalam panggilan metodeM1(i + 5), variabelintyang 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.ArrayTypeMismatchExceptionakan 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
FmenyebabkanSystem.ArrayTypeMismatchExceptiondilempar karena jenis elemen aktualbadalahstringdan bukanobject.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 = 3contoh 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
intdanstringditentukan 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 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 punXₑ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
- Setidaknya ada satu jenis variabel
- 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 jenisTⱼparameter yang sesuai di mana jenis output (§12.6.3.5) berisi variabel jenisXₑ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
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
Eadalah ekspresi tuple (§12.8.6) dengan aritasNdan elemenEᵢ, danTmerupakan jenis tuple dengan aritasNdengan jenisTₑelemen yang sesuai atauTmerupakan jenis nilai nullable danT0?merupakan jenisT0tuple dengan aritasNyang memiliki jenisTₑelemen yang sesuai , maka untuk setiapEᵢ, inferensi jenis input dibuat dariEᵢkeTₑ. - Jika
Eadalah fungsi anonim, inferensi jenis parameter eksplisit (§12.6.3.9) dibuat dariEkeT - Jika tidak, jika
Ememiliki jenisUdan 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
Ememiliki jenisUdan 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
Ememiliki jenisUdan parameter yang sesuai adalah parameter input (§15.6.2.3.2) danEmerupakan argumen input, maka inferensi yang tepat (§12.6.3.10) dibuat dariUkeT. - Jika tidak, jika
Ememiliki jenisUdan 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
Eadalah ekspresi tuple dengan aritasNdan elemenEᵢ, danTmerupakan jenis tuple dengan aritasNdengan jenisTₑelemen yang sesuai atauTmerupakan jenis nilai nullable danT0?merupakan jenisT0tuple dengan aritasNyang memiliki jenisTₑelemen yang sesuai , maka untuk setiapEᵢinferensi jenis output dibuat dariEᵢkeTₑ. - Jika
Eadalah fungsi anonim dengan tipeUpengembalian yang disimpulkan (§12.6.3.14) danTmerupakan tipe delegasi atau jenis pohon ekspresi dengan jenisTₓpengembalian, maka inferensi yang terikat lebih rendah (§12.6.3.11) dibuat dariUkeTₓ. - Jika tidak, jika
Eadalah kelompok metode danTadalah jenis delegasi atau jenis pohon ekspresi dengan jenis parameterT₁...Tᵥdan jenis pengembalianTₓ, dan resolusi overload dariEdengan jenisT₁...Tᵥmenghasilkan metode tunggal dengan jenis pengembalianU, maka inferensi batas bawah dibuat dariUkeTₓ. - Jika tidak, jika
Eadalah ekspresi dengan jenisU, 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
- Jika
Eadalah fungsi anonim yang diketik secara eksplisit dengan jenisU₁...Uᵥparameter danTmerupakan jenis delegasi atau jenis pohon ekspresi dengan jenisV₁...Vᵥparameter maka untuk setiapUᵢinferensi yang tepat (§12.6.3.10) dibuat dariUᵢke yang sesuaiVᵢ.
12.6.3.10 Inferensi yang tepat
Inferensi yang tepat dari tipe ke tipe U dibuat sebagai berikut:
- Jika
Vadalah salah satu dariXᵢyang belum diperbaiki, makaUditambahkan ke dalam kumpulan batas yang tepat untukXᵢ. - Jika tidak, set
V₁...VₑdanU₁...Uₑditentukan dengan memeriksa apakah salah satu kasus berikut berlaku:-
Vadalah jenis arrayV₁[...]danUadalah jenis arrayU₁[...]dengan peringkat yang sama -
Vadalah jenisV₁?danUadalah jenisU₁ -
Vadalah jenis yang dibentukC<V₁...Vₑ>danUadalah jenis yang dibentukC<U₁...Uₑ>
Jika salah satu kasus ini berlaku maka inferensi persis dibuat dari setiap keUᵢ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
Vadalah salah satu dariXᵢyang belum diperbaiki, makaUditambahkan ke dalam set batas bawah untukXᵢ. - Jika tidak, jika
Vadalah jenisV₁?danUadalah jenisU₁?maka inferensi terikat yang lebih rendah dibuat dariU₁keV₁. - Jika tidak, set
U₁...UₑdanV₁...Vₑditentukan dengan memeriksa apakah salah satu kasus berikut berlaku:-
Vadalah jenis arrayV₁[...]danUadalah jenis arrayU₁[...]dengan peringkat yang sama -
Vadalah salah satu dariIEnumerable<V₁>,ICollection<V₁>,IReadOnlyList<V₁>>,IReadOnlyCollection<V₁>, atauIList<V₁>, danUadalah jenis array satu dimensiU₁[] -
Vadalah jenisclass,struct,interface, ataudelegatedariC<V₁...Vₑ>yang dibangun dan ada jenis unikC<U₁...Uₑ>sedemikian rupa sehinggaU(atau, jikaUadalah jenisparameter, kelas dasar yang efektif atau anggota set antarmuka efektifnya) identik dengan,inheritsdari (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 dariUkeC<T>karenaU₁bisaXatauY.)
Jika salah satu kasus ini berlaku, inferensi dibuat dari setiapUᵢkeVᵢyang sesuai sebagai berikut: - Jika
Uᵢtidak diketahui sebagai jenis referensi, maka dibuat inferensi yang tepat - Jika tidak, jika
Uadalah jenis array, maka penyimpulan tentang batas bawah dilakukan. - Jika tidak, jika
VadalahC<V₁...Vₑ>maka inferensi tergantung pada parameter tipei-thdariC:- 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ₑdanU₁...Uₑditentukan dengan memeriksa apakah salah satu kasus berikut berlaku:-
Uadalah jenis arrayU₁[...]danVadalah jenis arrayV₁[...]dengan peringkat yang sama -
Uadalah salah satu dariIEnumerable<Uₑ>,ICollection<Uₑ>,IReadOnlyList<Uₑ>,IReadOnlyCollection<Uₑ>, atauIList<Uₑ>, danVadalah jenis array satu dimensiVₑ[] -
Uadalah jenisU1?danVadalah jenisV1? -
Uadalah jenis kelas, struktur, antarmuka, atau delegasi yang dibangun danC<U₁...Uₑ>danVadalah jenisclass, struct, interfaceataudelegateyangidenticalke,inheritsdari (secara langsung atau tidak langsung), atau mengimplementasikan (secara langsung atau tidak langsung) jenis unikC<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 dariC<U₁>keV<Q>. Inferensi tidak dibuat dariU₁keX<Q>atauY<Q>.)
Jika salah satu kasus ini berlaku, inferensi dibuat dari setiapUᵢkeVᵢyang sesuai sebagai berikut: - Jika
Uᵢtidak diketahui sebagai jenis referensi, maka dibuat inferensi yang tepat - Jika tidak, apabila
Vadalah tipe array, maka inferensi terikat atas dilakukan. - Jika tidak, jika
UadalahC<U₁...Uₑ>maka inferensi tergantung pada parameter tipei-thdariC:- 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
- Kumpulan jenis kandidat
Uₑdimulai sebagai kumpulan semua jenis dalam rentang batas untukXᵢ. - Setiap batas untuk
Xᵢdiperiksa secara bergantian: Untuk setiap batas tepat U dariXᵢ, maka semua jenisUₑyang tidak identik denganUdikeluarkan dari kumpulan kandidat. Untuk setiap batas bawahUdariXᵢsemua jenisUₑyang tidak memiliki konversi implisit dari dihapus dari set kandidat. Untuk setiap batas atas U dariXᵢ, semua tipeUₑyang tidak memiliki konversi implisit ke dihapus dari kumpulan kandidat. - Jika di antara jenis kandidat yang tersisa
Uₑada jenis unikVdi mana ada konversi implisit dari semua jenis kandidat lainnya, makaXᵢdiatur menjadiV. - 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
Fadalah ekspresi yang memiliki jenis, maka jenis pengembalian efektifFyang disimpulkan adalah jenis ekspresi tersebut. - Jika isi
Fadalah blok dan kumpulan ekspresi dalam pernyataan blokreturnmemiliki jenisTumum terbaik (§12.6.3.16), maka jenis pengembalian efektif yangFdisimpulkan adalahT. - Jika tidak, jenis pengembalian yang efektif tidak dapat disimpulkan untuk
F.
Jenis pengembalian yang disimpulkan ditentukan sebagai berikut:
- Jika
Fasinkron dan isinyaFadalah ekspresi yang diklasifikasikan sebagai tidak ada (§12.2), atau blok di mana tidak adareturnpernyataan yang memiliki ekspresi, jenis pengembalian yang disimpulkan adalah«TaskType»(§15.14.1). - Jika
Fasinkron dan memiliki jenisTpengembalian efektif yang disimpulkan , jenis pengembalian yang disimpulkan adalah«TaskType»<T>»(§15.14.1). - Jika
Ftidak bersifat asinkron dan memiliki jenis pengembalian efektif yang disimpulkan sebagaiT, maka jenis pengembalian yang disimpulkan adalahT. - Sebaliknya, tipe pengembalian tidak dapat disimpulkan untuk
F.
Contoh: Sebagai contoh inferensi jenis yang melibatkan fungsi anonim, pertimbangkan metode ekstensi
Selectyang dideklarasikan di kelasSystem.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.Linqdiimpor dengan arahanusing namespace, dan sebuah kelasCustomerdengan properti berjenisNamestring, metodeSelectdapat 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
Selectdiproses 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
TSourceadalahCustomer. Kemudian, menggunakan proses inferensi jenis fungsi anonim yang dijelaskan di atas,cdiberi jenisCustomer, dan ekspresic.Nameterkait dengan jenis pengembalian parameter pemilih, menyimpulkanTResultmenjadistring. Dengan demikian, pemanggilan setara denganSequence.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
Xmenjadistring. Kemudian, parameter fungsi anonim pertama,s, diberi jenis yang disimpulkanstring, dan ekspresiTimeSpan.Parse(s)terkait dengan jenis pengembalianf1, menyimpulkanYakanSystem.TimeSpan. Akhirnya, parameter fungsi anonim kedua,t, diberi jenis yang disimpulkanSystem.TimeSpan, dan ekspresit.TotalHoursterkait dengan jenis pengembalianf2, menyimpulkanZakandouble. Dengan demikian, hasil pemanggilan tersebut berjenisdouble.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 tetap
Xdiperkenalkan. - Untuk setiap ekspresi
Ei, inferensi jenis output (§12.6.3.8) dilakukan darinya keX. -
Xdiperbaiki (§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ᵥ)denganEᵢsebagai argumen dan menyimpulkanX. 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
Asesuai 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
Acocok dengan jumlah total parameter. JikaAmemiliki 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.
- mode pemindahan parameter dari argumen identik dengan mode pemindahan parameter dari parameter yang sesuai, dan:
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
thisdiizinkan §12.8.14.
- Jika grup metode dihasilkan dari suatu simple_name, metode instans hanya dapat berlaku jika akses
- 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ᵥkeQᵥtidak lebih baik daripada konversi implisit dariEᵥkePᵥ, dan - untuk setidaknya satu argumen, konversi dari
EᵥkePᵥlebih baik daripada konversi dariEᵥkeQᵥ.
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 danMₑadalah metode generik, makaMᵢlebih baik daripadaMₑ. - Jika tidak, jika
Mᵢberlaku dalam bentuk normalnya danMₑmemiliki array param dan hanya berlaku dalam bentuknya yang diperluas, makaMᵢlebih baik daripadaMₑ. - 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 paramMₑ, makaMᵢlebih baik daripadaMₑ. - Jika tidak, jika
Mᵥmemiliki jenis parameter yang lebih spesifik daripadaMₓ, makaMᵥlebih baik daripadaMₓ. Biarkan{R1, R2, ..., Rn}dan{S1, S2, ..., Sn}mewakili jenis parameterMᵥdanMₓyang tidak terinstansiasi dan tidak diperluas. jenis parameterMᵥlebih spesifik daripadaMₓjika, untuk setiap parameter,Rxtidak kurang spesifik daripadaSx, dan, setidaknya untuk satu parameter,Rxlebih spesifik daripadaSx:- 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 dalamMₓ, makaMᵥlebih baik daripadaMₓ. - Jika untuk setidaknya satu parameter
menggunakan pilihan penerusan parameter yang lebih baik ( §12.6.4.4 ) daripada parameter yang sesuai didan 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:
-
Esama persis denganT₁danEtidak sama persis denganT₂(§12.6.4.6) -
Esama persis denganT₁danT₂, danT₁adalah target konversi yang lebih baik daripadaT₂(§12.6.4.7) -
Eadalah grup metode (§12.2),T₁kompatibel (§21,4) dengan metode terbaik tunggal dari grup metode untuk konversiC₁, danT₂tidak kompatibel dengan metode terbaik tunggal dari grup metode untuk konversiC₂
12.6.4.6 Ekspresi yang cocok persis
Diberikan ekspresi E dan jenis T, Esama persis denganT jika salah satu ketentuan berikut terpenuhi:
-
Ememiliki jenisS, dan konversi identitas ada dariSkeT -
Eadalah fungsi anonim,Tadalah jenis delegasiDatau jenis pohon ekspresiExpression<D>dan salah satu dari berikut ini berlaku:- Jenis
Xpengembalian yang disimpulkan ada untukEdalam konteks daftarDparameter (§12.6.3.13), dan konversi identitas ada dariXke jenis pengembalianD -
Eadalah lambdaasynctanpa nilai pengembalian, danDmemiliki jenis pengembalian yang merupakan«TaskType»non-generik - Baik
Ebersifat non-asinkron danDmemiliki tipe pengembalianYatauEbersifat asinkron danDmemiliki tipe pengembalian«TaskType»<Y>(§15.14.1), dan salah satu yang berikut ini berlaku:- Isi
Eadalah ekspresi yang sama persis denganY - Isi
Eadalah blok di mana setiap statement 'return' menghasilkan ekspresi yang persis sama denganY
- Isi
- Jenis
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₁keT₂ada dan tidak ada konversi implisit dariT₂keT₁ada -
T₁adalah«TaskType»<S₁>(§15.14.1),T₂adalah«TaskType»<S₂>, danS₁merupakan target konversi yang lebih baik daripadaS₂ -
T₁adalah«TaskType»<S₁>(§15.14.1),T₂adalah«TaskType»<S₂>, danT₁lebih khusus daripadaT₂ -
T₁S₁atauS₁?di manaS₁adalah jenis integral yang ditandatangani, danT₂S₂atauS₂?di manaS₂adalah jenis integral yang tidak ditandatangani. Khusus:-
S₁adalahsbytedanS₂adalahbyte,ushort,uint, atauulong -
S₁shortdanS₂ushort,uint, atauulong -
S₁adalahintdanS₂adalahuint, atauulong -
S₁adalahlongdanS₂adalahulong
-
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
Fadalah 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
Fberlaku 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
Fdiganti pada langkah di atas, batasannya terpenuhi. - Jika
Fadalah metode statis, grup metode tidak akan dihasilkan oleh member_access yang penerimanya dikenal pada waktu kompilasi sebagai variabel atau nilai. - Jika
Fadalah 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
Madalah anggota fungsi statis:- Daftar argumen dievaluasi seperti yang dijelaskan dalam §12.6.2.
-
Mdiaktifkan.
- Jika tidak, jika jenisnya
Eadalah jenisVnilai , danMdinyatakan atau ditimpa dalamV:-
Edievaluasi. 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 iniEdiklasifikasikan sebagai variabel. - Jika
Etidak diklasifikasikan sebagai variabel, atau jikaVbukan jenis struct baca-saja (§16.2.2) danMbukan anggota fungsi readonly (§16.4.12), danEmerupakan salah satu dari:- parameter masuk (§15.6.2.3.2), atau
- bidang
readonly(§15.5.3), atau -
readonlyvariabel referensi atau pengembalian (§9,7), lalu variabel lokal sementara dariEjenis 's dibuat dan nilaiEditetapkan ke variabel tersebut.Ekemudian diklasifikasi ulang sebagai referensi ke variabel lokal sementara tersebut. Variabel sementara dapat diakses sebagaithisdalamM, tetapi tidak dengan cara lain. Dengan demikian, hanya ketikaEdapat ditulis adalah mungkin bagi pemanggil untuk mengamati perubahan yangMbuat untukthis.
- Daftar argumen dievaluasi seperti yang dijelaskan dalam §12.6.2.
-
Mdiaktifkan. Variabel yang dirujuk olehEmenjadi variabel yang dirujuk olehthis.
-
- Sebaliknya:
-
Edievaluasi. 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
Eadalah value_type, konversi pembungkusan (§10.2.9) dilakukan untuk mengonversiEke class_type, danEdianggap memiliki class_type tersebut dalam langkah-langkah berikut. Jika value_type adalah enum_type, maka class_type adalahSystem.Enum;; jika tidak, makaSystem.ValueType. - Nilai
Ediperiksa agar valid. Jika nilaiEnull,System.NullReferenceExceptionakan dilemparkan dan tidak ada langkah lebih lanjut yang dijalankan. - Implementasi anggota fungsi yang akan dipanggil ditentukan:
- Jika tipe pengikatan waktu
Eadalah antarmuka, anggota fungsi yang akan dipanggil adalah implementasiMyang disediakan oleh tipe waktu proses instans yang dirujuk olehE. Anggota fungsi ini ditentukan dengan menerapkan aturan pemetaan antarmuka (§19.6.5) untuk menentukan implementasiMyang disediakan oleh jenis run-time instans yang dirujuk olehE. - Jika
Madalah anggota fungsi virtual, maka fungsi yang akan dipanggil adalah implementasi dariMyang disediakan oleh tipe run-time dari instans yang dirujuk olehE. Anggota fungsi ini ditentukan dengan menerapkan aturan untuk menentukan implementasi yang paling turunan (§15,6,4) dariMsehubungan dengan jenis run-time instans yang dirujuk olehE. - Jika tidak,
Madalah fungsi anggota non-virtual, dan fungsi anggota yang akan dipanggil adalahMitu sendiri.
- Jika tipe pengikatan waktu
- Implementasi fungsi anggota yang ditentukan pada langkah di atas dipanggil. Objek yang dirujuk oleh
Emenjadi objek yang dirujuk oleh ini.
-
Catatan:§12.2 mengklasifikasikan akses properti sebagai memanggil anggota fungsi yang sesuai, baik
getaksesor atausetaksesor. 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.ValueTypeatauSystem.Enum. catatan 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
Eadalah ekspresi tuple dengan elemenn, hasil dekonstruksi adalah ekspresiEitu sendiri. - Selain itu, jika
Ememiliki tipe tuple(T1, ..., Tn)dengannelemen, makaEdievaluasi 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,
Etidak dapat didekonstruksi.
Di sini, __v dan __v1, ..., __vn mengacu pada variabel sementara yang tidak terlihat dan tidak dapat diakses.
Catatan: Ekspresi jenis
dynamictidak 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, danpointer_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) danSystem.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) danSystem.FormattableString(§C.3). Deskripsi format standar, yang identik untuk Regular_Interpolation_Format dan Verbatim_Interpolation_Format, dapat ditemukan dalam dokumentasi untukSystem.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 ≥ 1untuk setiap angkaIdari0hinggaN-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 kurung kurawal kiri (
- Karakter Interpolated_Regular_String_Mid atau Interpolated_Verbatim_String_Mid akan langsung mengikuti interpolasi yang sesuai, jika ada
- Spesifikasi tempat penampung:
- 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
Xyang memformat bilangan bulat sebagai heksadesimal dalam huruf besar, - format default untuk nilai
stringadalah 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
enol dan simple_name muncul dalam ruang deklarasi variabel lokal (§7,3) yang secara langsung berisi variabel lokal, parameter atau konstanta dengan namaI, maka simple_name mengacu pada variabel, parameter, atau konstanta lokal tersebut dan diklasifikasikan sebagai variabel atau nilai. - Jika
enol dan simple_name muncul dalam deklarasi metode generik tetapi berada di luar atribut dari method_declaration, dan jika deklarasi tersebut menyertakan parameter jenis dengan namaI, 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
enol dan deklarasiTmenyertakan parameter jenis dengan namaI, maka simple_name mengacu pada parameter jenis tersebut. - Jika tidak, jika pencarian anggota (§12.5)
IdiTdengan argumen jenisemenghasilkan kecocokan:- Jika
Tadalah 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 denganthis. 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 eadalah nol. - Jika tidak, hasilnya sama dengan akses anggota (§12.8.7) dari bentuk
T.IatauT.I<A₁, ..., Aₑ>.
- Jika
- Jika
- 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
eadalah nol danIadalah nama namespace layanan diN, maka:- Jika lokasi tempat simple_name muncul dikelilingi oleh deklarasi namespace untuk
Ndan deklarasi namespace berisi extern_alias_directive atau using_alias_directive yang mengaitkan namaIdengan namespace atau tipe, maka simple_name menjadi ambigu dan terjadi kesalahan waktu kompilasi. - Jika tidak, simple_name mengacu pada namespace bernama
IdiN.
- Jika lokasi tempat simple_name muncul dikelilingi oleh deklarasi namespace untuk
- Jika tidak, jika
Nberisi jenis yang dapat diakses dengan namaIdan parameter jenise, maka:- Jika
enol dan lokasi tempat simple_name terjadi diapit oleh deklarasi namespace untukNdan deklarasi namespace berisi extern_alias_directive atau using_alias_directive yang mengaitkan namaIdengan 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.
- Jika
- Kecuali jika lokasi tempat terjadinya simple_name diapit oleh deklarasi namespace untuk
N:- Jika
enol dan deklarasi namespace berisi extern_alias_directive atau using_alias_directive yang mengaitkan namaIdengan 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
Idan parameter tipee, 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
Idan parameter tipee, maka simple_name menjadi ambigu dan terjadi kesalahan saat kompilasi.
- Jika
Catatan: Seluruh langkah ini sama persis dengan langkah yang sesuai dalam pemrosesan namespace_or_type_name (§7,8). catatan akhir
- Jika
- Jika tidak, jika
enol danImerupakan 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,
amelaluif.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 inferredcontoh 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 harusTi Ni. - Jika tidak, jika
EiberbentukNiatauE.NiatauE?.Nimaka elemen jenis tuple adalahTi 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
NiatauE.NiatauE?.Ni. -
NiberformatItemX, di manaXadalah urutan digit desimal yang dimulai non-0yang dapat mewakili posisi elemen tuple, danXtidak mewakili posisi elemen.
- Satu elemen lain dari ekspresi tuple memiliki nama
- 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 typeDalam contoh ini, keempat ekspresi tuple adalah valid. Dua yang pertama,
t1dant2, tidak menggunakan tipe dari ekspresi tuple, sebaliknya menggunakan konversi tuple implisit. Dalam kasust2, konversi tuple implisit bergantung pada konversi implisit dari2kelongdan darinullkestring. Ekspresi tuple ketiga memiliki jenis(int i, string), dan oleh karena itu dapat diklasifikasikan ulang sebagai nilai dari jenis tersebut. Deklarasit4, 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
eadalah nol danEadalah namespace sertaEberisi namespace bersarang dengan namaI, maka hasilnya adalah namespace tersebut. - Jika tidak, jika
Eadalah namespace danEberisi tipe yang dapat diakses yang memiliki namaIdan parameter tipeK, maka hasilnya adalah tipe yang dibangun dengan argumen tipe yang diberikan. - Jika
Ediklasifikasikan sebagai jenis, jikaEbukan parameter jenis, dan jika pencarian anggota (§12,5)IdalamEdengan parameter jenisKmenghasilkan kecocokan, makaE.Idievaluasi dan diklasifikasikan sebagai berikut:Catatan: Ketika hasil pencarian anggota tersebut adalah grup metode dan
Kadalah nol, grup metode dapat berisi metode yang memiliki parameter jenis. Ini memungkinkan metode tersebut dapat dipertimbangkan untuk inferensi argumen jenis. catatan akhir- Jika
Imengidentifikasi jenis, maka hasilnya adalah jenis yang dibangun dengan argumen jenis tertentu. - Jika
Imengidentifikasi satu atau beberapa metode, maka hasilnya adalah grup metode tanpa ekspresi instans terkait. - Jika
Imengidentifikasi properti statis, hasilnya adalah akses properti tanpa ekspresi instans terkait. - Jika
Imengidentifikasi 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
IdiE. - Jika tidak, hasilnya adalah variabel, yaitu bidang statis
IdiE.
- 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
- Jika
Imengidentifikasi peristiwa statis:- Jika referensi terjadi dalam kelas atau struktur tempat peristiwa dideklarasikan, dan peristiwa dinyatakan tanpa event_accessor_declarations (§15,8,1), maka
E.Idiproses persis seolah-olahIadalah bidang statis. - Jika tidak, hasilnya adalah akses peristiwa tanpa ekspresi instans terkait.
- Jika referensi terjadi dalam kelas atau struktur tempat peristiwa dideklarasikan, dan peristiwa dinyatakan tanpa event_accessor_declarations (§15,8,1), maka
- Jika
Imengidentifikasi konstanta, maka hasilnya adalah nilai, yaitu nilai konstanta tersebut. - Jika
Imengidentifikasi anggota enumerasi, maka hasilnya adalah nilai, yaitu nilai anggota enumerasi tersebut. - Jika tidak,
E.Iadalah referensi anggota yang tidak valid, dan terjadi kesalahan waktu kompilasi.
- Jika
- Jika
Eadalah akses properti, akses pengindeks, variabel, atau nilai, yang jenisnyaT, dan pencarian anggota (§12,5)IdalamTdengan argumen jenisKmenghasilkan kecocokan, makaE.Idievaluasi dan diklasifikasikan sebagai berikut:- Pertama, jika
Eadalah akses properti atau pengindeks, maka nilai akses properti atau pengindeks diperoleh (§12.2.2) dan E diklasifikasikan ulang sebagai nilai. - Jika
Imengidentifikasi satu atau beberapa metode, maka hasilnya adalah grup metode dengan ekspresi instans terkaitE. - Jika
Imengidentifikasi properti instans, maka hasilnya adalah akses terhadap properti dengan ekspresi instans terkaitEdan jenis yang terkait adalah jenis dari properti tersebut. JikaTadalah tipe kelas, tipe yang terkait dipilih dari deklarasi pertama atau penimpaan properti yang ditemukan dengan memulai dariT, dan mencari melalui kelas dasarnya. - Jika
Tadalah jenis kelas danImengidentifikasi sebuah bidang instans dari jenis kelas tersebut:- Jika nilai
Eadalahnull, makaSystem.NullReferenceExceptionakan 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
Idalam objek yang direferensikan olehE. - Jika tidak, hasilnya adalah variabel, yaitu bidang
Idalam objek yang dirujuk olehE.
- Jika nilai
- Jika
Tadalah struct_type danImengidentifikasi bidang instans dari struct_typetersebut :- Jika
Eadalah 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 bidangIdalam instans struct yang diberikan olehE. - Jika tidak, hasilnya adalah variabel, yaitu bidang
Idalam instans struct yang diberikan olehE.
- Jika
- Jika
Imengidentifikasi 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-=, makaE.Idiproses persis seolah-olahIadalah bidang instans. - Jika tidak, hasilnya adalah akses event dengan ekspresi instance terkait
E.
- 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
- Pertama, jika
- Jika tidak, upaya dilakukan untuk memproses
E.Isebagai pemanggilan metode ekstensi (§12.8.10.3). Jika ini gagal,E.Iadalah 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 pengidentifikasiColoryang mengacu pada jenisColordibatasi oleh«...», dan yang mengacu pada bidangColortidak.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
Padalah tipe nilai nullable:Biarkan
Tmenjadi jenisP.Value.A.Jika
Tadalah parameter jenis yang tidak diketahui sebagai jenis referensi atau jenis nilai yang tidak dapat diubah ke null, kesalahan waktu kompilasi terjadi.Jika
Tadalah jenis nilai yang tidak dapat diubah ke null, maka jenisET?, dan artiEsama dengan arti:((object)P == null) ? (T?)null : P.Value.AKecuali bahwa
Pdievaluasi hanya sekali.Jika tidak, jenis dari
EadalahT, dan artiEsama dengan arti:((object)P == null) ? (T)null : P.Value.AKecuali bahwa
Pdievaluasi hanya sekali.
Sebaliknya:
Biarkan
Tmenjadi jenis ekspresiP.A.Jika
Tadalah parameter jenis yang tidak diketahui sebagai jenis referensi atau jenis nilai yang tidak dapat diubah ke null, kesalahan waktu kompilasi terjadi.Jika
Tadalah jenis nilai yang tidak dapat diubah ke null, maka jenisET?, dan artiEsama dengan arti:((object)P == null) ? (T?)null : P.AKecuali bahwa
Pdievaluasi hanya sekali.Jika tidak, jenis dari
EadalahT, dan artiEsama dengan arti:((object)P == null) ? (T)null : P.AKecuali bahwa
Pdievaluasi hanya sekali.
Catatan: Dalam ekspresi bentuk:
P?.A₀?.A₁kemudian jika
Pmenjadinull, maka baikA₀maupunA₁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
IsValidmengembalikantrue,pdapat dengan aman direferensikan untuk mengakses propertiName, 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 jikaxadalahnullpada runtime, pengecualian akan terjadi karenanulltidak dapat diubah keint.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, yaitulv, &,rv, adalahstring?, denganlvmenjadi parameter output, dan melakukan pengisian nilai sederhana.Metode
Mmeneruskan variabels, jenisstring, sebagai parameter outputAssign, pengkompilasi yang digunakan mengeluarkan peringatan karenasbukan variabel nullable. Mengingat bahwa argumen keduaAssigntidak 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 denganTdan 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 denganTdan 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
Fterkait dengan grup metodeM:- Jika
Fbukan generik,Fadalah kandidat saat:-
Mtidak memiliki daftar argumen tipe, dan -
Fberlaku sehubungan denganA(§12.6.4.2).
-
- Jika
Fumum danMtidak memiliki daftar argumen jenis,Fadalah 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
Fmemenuhi batasannya (§8,4,5), dan daftar parameterFberlaku sehubungan denganA(§12.6.4.2)
- Jika
Fumum danMmenyertakan daftar argumen jenis,Fadalah kandidat saat:-
Fmemiliki 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
Fmemenuhi batasannya (§8,4,5), dan daftar parameterFberlaku sehubungan denganA(§12.6.4.2).
-
- Jika
- Himpunan metode kandidat dikurangi hanya berisi metode dari jenis turunan paling akhir: Untuk setiap metode
C.Fdalam himpunan, di manaCadalah jenis di mana metodeFdideklarasikan, semua metode yang dideklarasikan dalam jenis dasarCdihapus dari himpunan. Selain itu, jikaCadalah jenis kelas selainobject, 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
objectdan 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 syaratMₑ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 syaratMₑ, maka kumpulan metode ekstensi tersebut adalah kumpulan kandidat.
- Jika namespace atau unit kompilasi yang diberikan secara langsung berisi deklarasi jenis non-generik
- 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.
-
Cadalah 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
nullini 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 metodeClebih 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.Glebih diutamakan daripadaC.G, danE.Flebih diutamakan daripada danD.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:
-
Ddievaluasi. Jika evaluasi ini menyebabkan pengecualian, tidak ada langkah lebih lanjut yang dijalankan. - Daftar argumen
Adievaluasi. Jika evaluasi ini menyebabkan pengecualian, tidak ada langkah lebih lanjut yang dijalankan. - Nilai
Ddiperiksa agar valid. Jika nilaiDadalahnull, makaSystem.NullReferenceExceptiondilempar dan tidak ada langkah lebih lanjut yang dijalankan. - Jika tidak,
Dadalah 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
Ediklasifikasikan sebagai apa-apa maka maknanya sama dengan arti blok :{ if ((object)P != null) PA; }kecuali bahwa
Pdievaluasi hanya sekali.Jika tidak, arti
Esama dengan arti blok :{ return E; }dan pada gilirannya arti blok ini tergantung pada apakah
Esecara 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 dynamiccontoh 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, atauulong; atau - untuk akses array peringkat tunggal saja, dari jenis
IndexatauRange; 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:
-
Pdievaluasi. 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,IndexatauRange; 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
Pdiperiksa agar valid. Jika nilaiPadalahnull, makaSystem.NullReferenceExceptiondilempar dan tidak ada langkah lebih lanjut yang dijalankan. - Jika langkah-langkah sebelumnya telah menghasilkan satu nilai indeks jenis
Rangemaka:- Biarkan L menjadi panjang array yang dirujuk oleh
P. -
Adiperiksa agar valid sehubungan dengan L (§18.3). Jika tidak makaSystem.ArgumentOutOfRangeExceptiondilemparkan dan tidak ada langkah-langkah lebih lanjut yang dijalankan. - Offset awal, S, dan jumlah item, N, sehubungan
Adengan L ditentukan seperti yang dijelaskan untukGetOffsetAndLength(§18.3). - Hasil dari akses array adalah array yang berisi salinan dangkal elemen N mulai dari
Pindeks S. Jika N nol, array memiliki elemen nol.
- Biarkan L menjadi panjang array yang dirujuk oleh
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
Rangenilai. 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.IndexOutOfRangeExceptiondilemparkan 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,IndexatauRange; 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:
-
Pdievaluasi. 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,IndexatauRange; 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
Pdiperiksa agar valid. Jika nilaiPadalahnull, makaSystem.NullReferenceExceptiondilempar dan tidak ada langkah lebih lanjut yang dijalankan. - Jika langkah-langkah sebelumnya telah menghasilkan nilai indeks jenis
Rangemaka:- Hasil evaluasi akses string adalah nilai jenis
string. - Biarkan L menjadi panjang string yang dirujuk oleh
P. -
Adiperiksa agar valid sehubungan dengan L (§18.3), jika tidak makaSystem.ArgumentOutOfRangeExceptiondilemparkan dan tidak ada langkah lebih lanjut yang dijalankan. - Offset awal, S, dan jumlah item, N, sehubungan
Adengan L ditentukan seperti yang dijelaskan untukGetOffsetAndLength(§18.3). - Hasil dari akses string adalah string yang dibentuk dengan menyalin karakter
Pmulai dari S, jika N adalah nol string kosong.
- Hasil evaluasi akses string adalah nilai jenis
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, akanSystem.IndexOutOfRangeExceptiondilemparkan dan tidak ada langkah lebih lanjut yang dijalankan. - Nilai karakter pada offset ekspresi indeks yang dikonversi dengan string
Pmenjadi hasil akses string.
- Hasil evaluasi akses string adalah nilai jenis
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
Ttelah dibentuk. Set ini terdiri dari semua pengindeks yang dideklarasikan dalamTatau jenis dasarTyang 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.Idalam set, di manaSadalah jenis di mana pengindeksIdinyatakan:- Jika
Itidak berlaku sehubungan denganA(§12.6.4.2), makaIdihapus dari set. - Jika
Iberlaku sehubungan denganA(§12.6.4.2), maka semua pengindeks yang dinyatakan dalam jenis dasarSdihapus dari set. - Jika
Iberlaku sehubungan denganA(§12.6.4.2) danSadalah jenis kelas selainobject, semua pengindeks yang dinyatakan dalam antarmuka dihapus dari set.
- Jika
- 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_expression
Pdievaluasi. - Ekspresi indeks argument_list
Adievaluasi secara berurutan, dari kiri ke kanan. - Menggunakan pengindeks terbaik yang ditentukan pada waktu pengikatan:
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
Padalah tipe nilai nullable:Biarkan
Tmenjadi jenis ekspresiP.Value[A]B.Jika
Tadalah parameter jenis yang tidak diketahui sebagai jenis referensi atau jenis nilai yang tidak dapat diubah ke null, kesalahan waktu kompilasi terjadi.Jika
Tadalah jenis nilai yang tidak dapat diubah ke null, maka jenisET?, dan artiEsama dengan arti:((object)P == null) ? (T?)null : P.Value[A]BKecuali bahwa
Pdievaluasi hanya sekali.Jika tidak, jenis dari
EadalahT, dan artiEsama dengan arti:((object)P == null) ? null : P.Value[A]BKecuali bahwa
Pdievaluasi hanya sekali.
Sebaliknya:
Biarkan
Tmenjadi jenis ekspresiP[A]B.Jika
Tadalah parameter jenis yang tidak diketahui sebagai jenis referensi atau jenis nilai yang tidak dapat diubah ke null, kesalahan waktu kompilasi terjadi.Jika
Tadalah jenis nilai yang tidak dapat diubah ke null, maka jenisET?, dan artiEsama dengan arti:((object)P == null) ? (T?)null : P[A]BKecuali bahwa
Pdievaluasi hanya sekali.Jika tidak, jenis dari
EadalahT, dan artiEsama dengan arti:((object)P == null) ? null : P[A]BKecuali bahwa
Pdievaluasi hanya sekali.
Catatan: Dalam ekspresi bentuk:
P?[A₀]?[A₁]jika
Pmengevaluasi menjadinullmaka baikA₀maupunA₁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
thisdigunakan 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
thisdigunakan 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
thisdigunakan 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
thisberperilaku 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
thisberperilaku sama persis dengan parameterrefdari jenis struct. Secara khusus, ini berarti bahwa variabel dianggap awalnya ditetapkan.
- Jika deklarasi konstruktor tidak memiliki inisialisasi konstruktor, variabel
- Saat
thisdigunakan 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),
thisvariabel mewakili struktur tempat metode atau aksesor dipanggil.- Jika struct adalah
readonly struct, variabelthisberperilaku sama persis dengan parameter input dari jenis struct - Jika tidak, variabel
thisberperilaku sama persis dengan parameterrefdari jenis struct
- Jika struct adalah
- 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.
- Jika metode atau aksesor bukan iterator (§15,15) atau fungsi asinkron (§15.14),
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,basebukan 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
xdiklasifikasikan sebagai variabel:-
xdievaluasi untuk menghasilkan variabel. - Nilai
xdisimpan. - Nilai
xyang disimpan dikonversi ke jenis operand operator yang dipilih dan operator dipanggil dengan nilai ini sebagai argumennya. - Nilai yang dikembalikan oleh operator dikonversi ke jenis
xdan disimpan di lokasi yang diberikan oleh evaluasi sebelumnyax. - Nilai
xyang disimpan menjadi hasil operasi.
-
- Jika
xdiklasifikasikan sebagai akses properti atau pengindeks:- Ekspresi instans (jika
xtidakstatic) dan daftar argumen (jikaxadalah akses pengindeks) yang terkait denganxdievaluasi, dan hasilnya digunakan dalam pemanggilan get dan set aksesor berikutnya. - Aksesor get
xdipanggil dan nilai yang dikembalikan disimpan. - Nilai
xyang disimpan dikonversi ke jenis operand operator yang dipilih dan operator dipanggil dengan nilai ini sebagai argumennya. - Nilai yang dikembalikan oleh operator dikonversi ke jenis
xdan aksesor setxdipanggil dengan nilai ini sebagai argumen nilainya. - Nilai
xyang disimpan menjadi hasil operasi.
- Ekspresi instans (jika
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
Tadalah value_type danAtidak ada:-
object_creation_expression adalah pemanggilan konstruktor bawaan. Hasil dari object_creation_expression adalah nilai jenis
T, yaitu nilai default untukTseperti yang didefinisikan dalam §8,3,3.
-
object_creation_expression adalah pemanggilan konstruktor bawaan. Hasil dari object_creation_expression adalah nilai jenis
- Jika tidak, jika
Tadalah type_parameter danAtidak 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 ada batasan jenis nilai atau batasan konstruktor (§15.2.5) telah ditentukan untuk
- Jika tidak, jika
Tadalah class_type atau struct_type:- Jika
Tadalah 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 denganA(§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.
- Jika
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
Tadalah class_type:- Sebuah instance baru dari kelas
Tdialokasikan. Jika tidak ada cukup memori yang tersedia untuk mengalokasikan instans baru,System.OutOfMemoryExceptiondilemparkan 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.
- Sebuah instance baru dari kelas
- Jika
Tadalah struct_type:- Instans jenis
Tdibuat 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.
- Instans jenis
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
Pointdapat 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
__aadalah 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
Rectangledapat 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,__p1dan__p2adalah variabel sementara yang tidak terlihat dan tidak dapat diakses.Jika konstruktor
Rectanglemengalokasikan dua instansPointyang disematkan, konstruktor tersebut dapat digunakan untuk menginisialisasi instansPointyang 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
Pointyang 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,__c1dan__c2adalah 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
p1danp2memiliki 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 jenisint[,], dan ekspresi pembuatan arrayint[10][,]baru menghasilkan instans array jenisint[][,]. 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.OverflowExceptiondilemparkan 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.OutOfMemoryExceptiondilemparkan 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 elemennull. Tidak mungkin bagi ekspresi pembuatan array yang sama untuk juga menginstansiasi sub-array, dan pernyataanint[][] a = new int[100][5]; // Errormenghasilkan 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" }; // ErrorEkspresi terakhir menyebabkan kesalahan waktu kompilasi karena tidak
intataustringsecara 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 sebagaiobject[]. 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
Eadalah grup metode, ekspresi pembuatan delegasi diproses dengan cara yang sama seperti konversi grup metode (§10,8) dariEkeD.Jika
Eadalah fungsi anonim, ekspresi pembuatan delegasi diproses dengan cara yang sama seperti konversi fungsi anonim (§10,7) dariEkeD.Jika
Eadalah nilai,Eharus kompatibel (§21.2) denganD, dan hasilnya adalah referensi ke delegasi yang baru dibuat dengan daftar pemanggilan entri tunggal yang memanggilE.
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
Eadalah grup metode, ekspresi pembuatan delegasi dievaluasi sebagai konversi grup metode (§10,8) dariEkeD. - Jika
Eadalah fungsi anonim, pembuatan delegasi dievaluasi sebagai konversi fungsi anonim dariEkeD(§10,7). - Jika
adalah nilai dari tipe delegasi : -
Edievaluasi. Jika evaluasi ini menyebabkan pengecualian, tidak ada langkah lebih lanjut yang dijalankan. - Jika nilai
Eadalahnull, makaSystem.NullReferenceExceptiondilempar dan tidak ada langkah lebih lanjut yang dijalankan. - Sebuah instance baru dari jenis delegate
Ddialokasikan. Jika tidak ada cukup memori yang tersedia untuk mengalokasikan instans baru,System.OutOfMemoryExceptiondilemparkan 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.fdiinisialisasi dengan delegasi yang mengacu pada metodeSquarekedua karena metode tersebut sama persis dengan daftar parameter dan jenis pengembalianDoubleFunc. Jika metodeSquarekedua 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
objectyang 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.Typekhusus 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 metodevoid, dengan instansSystem.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
intdanSystem.Int32adalah jenis yang sama. Hasiltypeof(X<>)tidak bergantung pada argumen jenis tetapi hasiltypeof(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
floatataudoubleke 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:
-
checkedDalam konteks, jika operasi adalah ekspresi konstanta (§12,25), terjadi kesalahan waktu kompilasi. Jika tidak, ketika operasi dilakukan pada run-time,System.OverflowExceptiondihasilkan. - 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
FmengeluarkanSystem.OverflowException, dan metodeGmengembalikan –727379968 (32 bit paling rendah dari hasil yang berada di luar rentang). Perilaku metodeHtergantung pada konteks pemeriksaan luapan default untuk kompilasi, tetapi sama denganFatau sama denganG.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
FdanHmenyebabkan kesalahan pada saat kompilasi dilaporkan, karena ekspresi dievaluasi dalam kontekschecked. Luapan juga terjadi saat mengevaluasi ekspresi konstanta dalamG, tetapi karena evaluasi berlangsung dalam konteksunchecked, 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
checkeddi F tidak memengaruhi evaluasix * ydiMultiply, sehinggax * ydievaluasi 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 rentangint, tanpa operatorunchecked, transmisi keintakan menghasilkan kesalahan waktu kompilasi.contoh akhir
Catatan: Operator dan pernyataan
checkeddanuncheckedmemungkinkan 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
Lengthdari 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,stackallocmenghasilkanSpan<int>, yang dikonversi oleh operator implisit keReadOnlySpan<int>. Demikian pula, untukspan9,Span<double>yang dihasilkan dikonversi ke jenis yang ditentukan penggunaWidget<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 generikList<T>dideklarasikan dalam namespaceSystem.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 padanameof(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) -
dynamicuntuk 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
Xdari nol. Jika nilaiXadalah nilai terkecil yang dapat diwakili dari jenis operand (−2³¹ untukintatau −2⁶³ untuklong), maka negasi matematikaXtidak dapat diwakili dalam jenis operand. Jika ini terjadi dalam kontekschecked,System.OverflowExceptionakan dilemparkan; jika terjadi dalam konteksunchecked, hasilnya adalah nilai operand dan luapan tidak dilaporkan.Jika operan operator negasi berjenis
uint, itu dikonversi ke jenislong, dan jenis hasilnyalong. Pengecualian adalah aturan yang mengizinkan nilaiint−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 nilailong−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
Xdengan tanda terbalik. JikaxadalahNaN, maka hasilnya jugaNaN.Negasi yang desimal
decimal operator –(decimal x);Hasilnya dihitung dengan mengurangi
Xdari nol. Negasi dari bilangan desimal setara dengan menggunakan operator minus unar dari jenisSystem.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
xdiklasifikasikan sebagai variabel:-
xdievaluasi untuk menghasilkan variabel. - Nilai
xdikonversi 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 evaluasixdan menjadi hasil operasi.
-
- Jika
xdiklasifikasikan sebagai akses properti atau pengindeks:- Ekspresi instans (jika
xtidakstatic) dan daftar argumen (jikaxadalah akses pengindeks) yang terkait denganxdievaluasi, dan hasilnya digunakan dalam pemanggilan get dan set aksesor berikutnya. - Aksesor get
xdipanggil. - 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 setxdipanggil dengan nilai ini sebagai argumen nilainya. - Nilai ini juga merupakan hasil operasi.
- Ekspresi instans (jika
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)–ydapat ditafsirkan sebagai cast_expression (pemeran–yuntuk mengetikx) atau sebagai additive_expression dikombinasikan dengan parenthesized_expression (yang menghitung nilaix – 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) kecualiasdanis.
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
xdanyadalah pengidentifikasi, makax.yadalah tata bahasa yang benar untuk jenis, bahkan jikax.ytidak benar-benar menunjukkan jenis. contoh akhir
Catatan: Dari aturan disambiguasi, dapat disimpulkan bahwa, jika
xdanyadalah pengidentifikasi, maka(x)y,(x)(y), dan(x)(-y)cast_expression, tetapi(x)-ytidak, walaupunxmengidentifikasi jenis. Namun, jikaxadalah kata kunci yang mengidentifikasi jenis yang telah ditentukan sebelumnya (sepertiint), 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:
-
tadalah jenis pada saat kompilasidynamic -
tmemiliki instans atau metode ekstensi yang dapat diakses yang disebutGetAwaitertanpa parameter dan tanpa parameter jenis, dan jenis pengembalianAuntuk mana semua hal berikut berlaku:-
Amengimplementasikan antarmukaSystem.Runtime.CompilerServices.INotifyCompletion(selanjutnya dikenal sebagaiINotifyCompletionuntuk singkat) -
Amemiliki properti instansIsCompletedyang dapat diakses dan mudah dibaca bertipebool -
Amemiliki metode instans yang dapat diaksesGetResulttanpa 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
aawaiter diperoleh dengan mengevaluasi ekspresi(t).GetAwaiter(). -
boolbdiperoleh dengan mengevaluasi ekspresi(a).IsCompleted. - Jika
bfalsemaka evaluasi bergantung pada apakahamenggunakan antarmukaSystem.Runtime.CompilerServices.ICriticalNotifyCompletion(selanjutnya dikenal sebagaiICriticalNotifyCompletionuntuk singkatnya). Pemeriksaan ini dilakukan pada waktu pengikatan; yaitu, pada run-time jikaamemiliki jenis waktu kompilasidynamic, dan pada waktu kompilasi jika tidak. Biarkanrmenunjukkan delegasi kelanjutan (§15.14):- Jika
atidak menerapkanICriticalNotifyCompletion, ekspresi((a) as INotifyCompletion).OnCompleted(r)dievaluasi. - Jika
amenerapkanICriticalNotifyCompletion, ekspresi((a) as ICriticalNotifyCompletion).UnsafeOnCompleted(r)dievaluasi. - Evaluasi kemudian ditangguhkan, dan kontrol dikembalikan ke pemanggil fungsi asinkron saat ini.
- Jika
- Entah segera setelah (jika
badalahtrue), atau setelah pemanggilan delegasi pelanjutan nanti (jikabadalahfalse), 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:
-
xadalah operan kiri jika ada, jika tidak, ekspresi0; dan -
yadalah 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.OverflowExceptiondilemparkan. Dalam konteksunchecked, 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,
xdanyadalah nilai terbatas positif.zadalah hasilx * y, dibulatkan ke nilai terdekat yang dapat diwakili. Jika besarnya hasil terlalu besar untuk jenis tujuan,ztidak terbatas. Karena pembulatan,zmungkin nol meskipun baikxmaupunytidak nol.+y-y+0-0+∞-∞NaN+x+z-z+0-0+∞-∞NaN-x-z+z-0+0-∞+∞NaN+0+0-0+0-0NaNNaNNaN-0-0+0-0+0NaNNaNNaN+∞+∞-∞NaNNaN+∞-∞NaN-∞-∞+∞NaNNaN-∞+∞NaNNaNNaNNaNNaNNaNNaNNaNNaN(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.OverflowExceptiondilemparkan. 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 jenisSystem.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.DivideByZeroExceptionakan 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
intataulongterkecil yang dapat diwakili dan operand kanan–1, terjadi luapan. Dalam kontekschecked, ini menyebabkanSystem.ArithmeticException(atau subkelasnya) dilemparkan. Dalam konteksunchecked, itu ditentukan oleh implementasi apakahSystem.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,
xdanyadalah nilai terbatas positif.zadalah hasilx / y, dibulatkan ke nilai terdekat yang dapat diwakili.+y-y+0-0+∞-∞NaN+x+z-z+∞-∞+0-0NaN-x-z+z-∞+∞-0+0NaN+0+0-0NaNNaN+0-0NaN-0-0+0NaNNaN-0+0NaN+∞+∞-∞+∞-∞NaNNaNNaN-∞-∞+∞-∞+∞NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNPembagian desimal:
decimal operator /(decimal x, decimal y);Jika nilai operand kanan adalah nol,
System.DivideByZeroExceptionakan dilemparkan. Jika besarnya nilai yang dihasilkan terlalu besar untuk diwakili dalam format desimal,System.OverflowExceptiondilemparkan. 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 skalaxlebih sedikit skalay.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 % yadalah nilai yang dihasilkan olehx – (x / y) * y. Jikaynol,System.DivideByZeroExceptionakan dilemparkan.Jika operand kiri adalah nilai
intataulongterkecil dan operand kanan adalah–1, makaSystem.OverflowExceptionterjadi jika dan hanya jikax / yakan 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,
xdanyadalah nilai terbatas positif.zadalah hasil darix % ydan dihitung sebagaix – n * y, di mana n adalah bilangan bulat terbesar yang mungkin kurang dari atau sama denganx / y. Metode komputasi sisa ini dianalogikan dengan yang digunakan untuk operan bilangan bulat, tetapi berbeda dari definisi IEC 60559 (di mananadalah bilangan bulat yang paling dekat denganx / y).+y-y+0-0+∞-∞NaN+x+z+zNaNNaN+x+xNaN-x-z-zNaNNaN-x-xNaN+0+0+0NaNNaN+0+0NaN-0-0-0NaNNaN-0-0NaN+∞NaNNaNNaNNaNNaNNaNNaN-∞NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNSisa desimal:
decimal operator %(decimal x, decimal y);Jika nilai operand kanan adalah nol,
System.DivideByZeroExceptionakan dilemparkan. Ditentukan oleh implementasi kapanSystem.ArithmeticException(atau subkelasnya) dilemparkan. Implementasi yang sesuai tidak akan memberikan pengecualian untukx % ydalam hal apa pun di manax / ytidak memberikan pengecualian. Skala hasil, sebelum pembulatan apa pun, adalah yang lebih besar dari skala dua operan, dan tanda hasilnya, jika bukan nol, sama denganx.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.OverflowExceptionakan dilemparkan. Dalam konteksunchecked, 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,
xdanyadalah nilai terbatas bukan nol, danzadalah hasil darix + y. Jikaxdanymemiliki besaran yang sama tetapi tanda yang berlawanan,zadalah nol positif. Jikax + yterlalu besar untuk diwakili dalam jenis tujuan,zadalah tak terbatas dengan tanda yang sama denganx + y.y+0-0+∞-∞NaNxzxx+∞-∞NaN+0y+0+0+∞–∞NaN-0y+0-0+∞-∞NaN+∞+∞+∞+∞+∞NaNNaN-∞-∞-∞-∞NaN-∞NaNNaNNaNNaNNaNNaNNaNNaNPenambahan desimal:
decimal operator +(decimal x, decimal y);Jika besarnya nilai yang dihasilkan terlalu besar untuk diwakili dalam format desimal,
System.OverflowExceptiondilemparkan. 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
Eadalah jenis enum, danUadalah jenisEyang 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 adalahnull, maka string kosong akan digunakan sebagai pengganti. Jika tidak, setiap operand non-stringdikonversi ke representasi stringnya dengan memanggil metodeToStringvirtual yang diwarisi dari jenisobject. JikaToStringmengembalikannull, 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
ToStringyang secara implisit dipanggil selama eksekusi mungkin dipengaruhi oleh setelan lokal.contoh akhir
Hasil dari operator perangkaian string adalah
stringyang terdiri dari karakter operand kiri diikuti oleh karakter operand kanan. Operator penggabungan string tidak pernah mengembalikan nilainull.System.OutOfMemoryExceptiondapat 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
Dadalah jenis delegasi:D operator +(D x, D y);Jika operand pertama
null, hasil operasi adalah nilai operand kedua (bahkan jika itu juganull). Jika tidak, jika operand kedua adalahnull, 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 yDalam konteks
checked, jika perbedaannya berada di luar rentang jenis hasil,System.OverflowExceptionakan dilemparkan. Dalam konteksunchecked, 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,
xdanyadalah nilai terbatas bukan nol, danzadalah hasil darix – y. Jikaxdanysama,zadalah nol positif. Jikax – yterlalu besar untuk diwakili dalam jenis tujuan,zadalah tak terbatas dengan tanda yang sama denganx – y.y+0-0+∞-∞NaNxzxx-∞+∞NaN+0-y+0+0-∞+∞NaN-0-y-0+0-∞+∞NaN+∞+∞+∞+∞NaN+∞NaN-∞-∞-∞-∞-∞NaNNaNNaNNaNNaNNaNNaNNaNNaN(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.OverflowExceptiondilemparkan. 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
Eadalah jenis enum, danUadalah jenisEyang 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 ordinalxdany, 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
Dadalah jenis delegasi:D operator –(D x, D y);Semantiknya adalah sebagai berikut:
- Jika operand pertama adalah
null, maka hasil operasinya adalahnull. - 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.
- Jika daftar dibandingkan sama, seperti yang ditentukan oleh operator kesetaraan delegasi (§12.14.9), hasil operasinya adalah
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
- Jika operand pertama adalah
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
<<menggeserxke kiri oleh sejumlah bit yang dihitung seperti dijelaskan di bawah ini.Bit berurutan tinggi di luar rentang jenis hasil
xdibuang, 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
>>menggeserxke kanan sejumlah bit yang dihitung seperti yang dijelaskan di bawah ini.Ketika
xberjenisintataulong, bit urutan rendahxdibuang, bit yang tersisa digeser ke kanan, dan posisi bit kosong urutan tinggi diatur ke nol jikaxtidak negatif dan diatur ke satu jikaxnegatif.Ketika
xberjenisuintatauulong, bit urutan rendahxdibuang, 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
xadalahintatauuint, jumlah pergeseran ditentukan oleh lima bit urutan rendah daricount. Dengan kata lain, jumlah shift dihitung daricount & 0x1F. - Ketika jenis
xadalahlongatauulong, jumlah shift diberikan oleh enam bit paling rendah daricount. Dengan kata lain, jumlah shift dihitung daricount & 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
xadalah variabel jenisint, operasiunchecked ((int)((uint)x >> y))melakukan pergeseran logis ke kananx. 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
isharus 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_expressionsisiiskanan ; 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
xdanyadalah NaN, makax < 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 jenisTdi manaTadalah type_parameter yang tidak diketahui sebagai jenis nilai, dan tidak memiliki batasan jenis nilai.- Jika pada waktu runtime
Tadalah jenis nilai yang tidak dapat bernilai null, hasil dari==adalahfalse, dan hasil dari!=adalahtrue. - Jika pada runtime
Tadalah jenis nilai nullable, hasilnya dihitung dariHasValueproperti operand, seperti yang dijelaskan dalam (§12.14.10). - Jika saat runtime
Tadalah tipe referensi, hasilnya adalahtruejika operand adalahnull, danfalsesebaliknya.
- Jika pada waktu runtime
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 == yataux != y, jika terdapatoperator ==atauoperator !=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 tipeobject.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 == nulldiizinkan meskipunTdapat mewakili jenis nilai yang tidak dapat diubah ke null, dan hasilnya hanya didefinisikanfalseketikaTadalah 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 FalseVariabel
sdantmengacu pada dua instans string yang berbeda yang berisi karakter yang sama. Output perbandinganTruepertama karena operator kesetaraan string yang telah ditentukan sebelumnya (§12.14.8) dipilih ketika kedua operan berjenisstring. Perbandingan yang tersisa semuanya mengeluarkanFalsekarena overloadoperator ==dalam tipestringtidak berlaku ketika salah satu operand memiliki tipe waktu pengikatanobject.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
Falsekarena pemaksaan menciptakan referensi ke dua instans terpisah dari nilaiintyang 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-
nullke 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 adalahnull. - 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-
nullsebagai 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
xdievaluasi. - Operan di sisi kanan
ydievaluasi. - Untuk setiap pasangan elemen
xidanyidalam urutan leksikal:- Operator
xi == yidievaluasi, dan hasil dari jenisbooldiperoleh dengan cara berikut:- Jika perbandingan menghasilkan
boolmaka itulah hasilnya. - Jika tidak, jika perbandingan menghasilkan
dynamicmaka operatorfalsedipanggil secara dinamis di atasnya, dan nilaiboolyang 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 nilaiboolyang dihasilkan dinegasikan dengan operator negasi logis (!).
- Jika perbandingan menghasilkan
- Jika
boolbernilaifalse, maka tidak ada evaluasi lebih lanjut yang terjadi, dan hasil operator kesetaraan susunan adalahfalse.
- Operator
- Jika setiap perbandingan elemen menghasilkan
true, maka hasil operator kesetaraan tuple adalahtrue.
Operator kesamaan tuple x != y dievaluasi sebagai berikut:
- Operand di sisi kiri
xdievaluasi. - Operan di sisi kanan
ydievaluasi. - Untuk setiap pasangan elemen
xidanyidalam urutan leksikal:- Operator
xi != yidievaluasi, dan hasil dari jenisbooldiperoleh dengan cara berikut:- Jika perbandingan menghasilkan
boolmaka itulah hasilnya. - Jika tidak, jika perbandingan menghasilkan
dynamicmaka operatortruedipanggil secara dinamis di atasnya, dan nilaiboolyang 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 nilaiboolyang dihasilkan adalah hasilnya.
- Jika perbandingan menghasilkan
- Jika
boolbernilaitrue, maka tidak ada evaluasi lebih lanjut yang terjadi, dan hasil operator kesetaraan susunan adalahtrue.
- Operator
- Jika setiap perbandingan elemen menghasilkan
false, maka hasil operator kesetaraan tuple adalahfalse.
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:
- Jika
Eadalah fungsi anonim atau grup metode, kesalahan waktu kompilasi terjadi. - Jika
Tadalah jenis referensi nullable (§8.9.3), kesalahan waktu kompilasi terjadi. - Jika
Eadalahnullharfiah, atau jika nilaiEnull, hasilnya adalahfalse. - Sebaliknya:
- Anggaplah
Radalah jenis runtime dariE. - Mari kita
Dberasal dariRsebagai berikut:- Jika
Radalah jenis nilai yang dapat diubah ke null,Dadalah jenisRyang mendasar. - Jika tidak,
DadalahR.
- Jika
- Hasilnya tergantung pada
DdanTsebagai berikut:- Jika
Tadalah jenis referensi, hasilnya adalahtruejika:- konversi identitas ada antara
DdanT, atau -
Dadalah jenis referensi dan konversi referensi implisit dariDkeTada, atau -
Dadalah jenis nilai dan konversi tinju dariDkeTyang ada.
- konversi identitas ada antara
- Jika
Tadalah tipe data yang bisa bernilai null, hasilnya adalahtruejikaDadalah jenis dasar dariT. - Jika
Tadalah jenis nilai yang tidak dapat diubah ke null, hasilnyatruejikaDdanTadalah jenis yang sama. - Jika tidak, hasilnya
false.
- Jika
- Anggaplah
Konversi yang ditentukan pengguna tidak dipertimbangkan oleh operator is.
Catatan: Karena operator
isdievaluasi pada runtime, semua argumen jenis telah diganti dan tidak ada jenis terbuka (§8.4.3) untuk dipertimbangkan. catatan akhir
Catatan: Operator
isdapat dipahami dalam hal jenis dan konversi waktu kompilasi sebagai berikut, di manaCadalah jenis waktu kompilasiE:
- Jika jenis waktu kompilasi
esama denganT, 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 kompilasiEkeT:
- Jika
Cadalah jenis nilai yang tidak dapat diubah ke null, hasil operasitrue.- 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
CkeT, atau jikaCatauTmerupakan jenis terbuka (§8.4.3), maka pemeriksaan runtime seperti di atas akan dilakukan.- Jika tidak, referensi, pembungkusan, atau konversi dari
Eke tipeTtidak mungkin dilakukan, dan hasil dari operasi tersebut adalahfalse. 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.
-
Etidak menunjuk nilai atau tidak memiliki tipe. - Pola
Ptidak berlaku (§11,2) ke jenisT.
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:
- Identitas (§10.2.2), nullable implisit (§10.2.6), referensi implisit (§10.2.8), boxing (§10.2.9), nullable eksplisit (§10.3.4), referensi eksplisit (§10.3.5), atau pembungkusan (§8.3.12) terdapat dari
EkeT. - Jenis
EatauTadalah jenis terbuka. -
Eadalah harfiahnull.
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
TGdiketahui sebagai jenis referensi, karena memiliki batasan kelas. Jenis parameterUdariHtidak sesuai; oleh karena itu, penggunaan operatorasdiHtidak 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 && ysesuai dengan operasix & y, kecuali bahwaydievaluasi hanya jikaxtidakfalse. - Operasi
x || ysesuai dengan operasix | y, kecuali bahwaydievaluasi hanya jikaxtidaktrue.
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 truemengembalikanfalsedanoperator falsemengembalikanfalse. 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 && ydievaluasi sebagaix ? y : false. Dengan kata lain,xpertama kali dievaluasi dan dikonversi ke jenisbool. Kemudian, jikaxtrue,ydievaluasi dan dikonversi ke jenisbool, dan ini menjadi hasil operasi. Jika tidak, hasil operasi adalahfalse. - Operasi
x || ydievaluasi sebagaix ? true : y. Dengan kata lain,xpertama kali dievaluasi dan dikonversi ke jenisbool. Kemudian, jikaxtrue, hasil operasi adalahtrue. Jika tidak,ydievaluasi dan diubah menjadi tipebool, 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 jenisT, dan harus mengembalikan hasil dari jenisT. -
Takan berisi pernyataanoperator truedanoperator 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 && ydievaluasi sebagaiT.false(x) ? x : T.&(x, y), di manaT.false(x)adalah pemanggilanoperator falseyang dideklarasikan dalamT, danT.&(x, y)adalah pemanggilanoperator &yang dipilih. Dengan kata lain,xpertama kali dievaluasi danoperator falsedipanggil pada hasil untuk menentukan apakahxpasti salah. Kemudian, jikaxpasti salah, hasil operasi adalah nilai yang sebelumnya dihitung untukx. Jika tidak,ydievaluasi, kemudianoperator &yang dipilih dipanggil dengan menggunakan nilai yang telah dihitung sebelumnya untukxdan nilai yang dihitung untukyuntuk menghasilkan hasil dari operasi tersebut. - Operasi
x || ydievaluasi sebagaiT.true(x) ? x : T.|(x, y), di manaT.true(x)adalah pemanggilanoperator trueyang dideklarasikan dalamT, danT.|(x, y)adalah pemanggilanoperator |yang dipilih. Dengan kata lain,xpertama kali dievaluasi danoperator truedipanggil pada hasil untuk menentukan apakahxpasti benar. Kemudian, jikaxpasti benar, hasil operasi adalah nilai yang sebelumnya dihitung untukx. Jika tidak,ydievaluasi, kemudianoperator |yang dipilih dipanggil dengan menggunakan nilai yang telah dihitung sebelumnya untukxdan nilai yang dihitung untukyuntuk 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 ?? cdievaluasi sebagaia ?? (b ?? c). Secara umum, ekspresi dengan bentukE1 ?? E2 ?? ... ?? ENmengembalikan operan pertama yang bukannull, ataunulljika semua operan adalahnull. 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
Aada 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
Aada danbadalah ekspresi dinamis, jenis hasilnya adalahdynamic. Saat runtime,adievaluasi terlebih dahulu. Jikaatidaknull,adikonversi kedynamic, dan ini menjadi hasilnya. Jika tidak,bdievaluasi, dan ini menjadi hasilnya. - Jika tidak, jika
Aada dan merupakan tipe nilai yang dapat bernilai null dan konversi implisit ada daribkeA₀, maka jenis hasilnya adalahA₀. Saat runtime,adievaluasi terlebih dahulu. Jikaatidaknull,adibongkar untuk mengetikA₀, dan ini menjadi hasilnya. Jika tidak,bdievaluasi dan dikonversi ke jenisA₀, dan ini menjadi hasilnya. - Jika
Aada dan konversi implisit daribkeAada, maka jenis hasilnya adalahA. Saat runtime,adievaluasi terlebih dahulu. Jikaatidaknull,amenjadi hasilnya. Jika tidak,bdievaluasi dan dikonversi ke jenisA, dan ini menjadi hasilnya. - Jika
Aada dan merupakan tipe nilai yang dapat bernilai null, sertabmemiliki tipeBdan ada konversi implisit dariA₀keB, maka tipe hasilnya adalahB. Saat runtime,adievaluasi terlebih dahulu. Jikaatidaknull,adiurai ke jenisA₀dan dikonversi ke jenisB, dan ini menjadi hasilnya. Jika tidak,bdievaluasi dan menjadi hasilnya. - Sebaliknya, jika
bmemiliki jenisBdan konversi implisit ada dariakeB, maka jenis hasilnya adalahB. Saat runtime,adievaluasi terlebih dahulu. Jikaatidaknull,adikonversi ke jenisB, dan ini menjadi hasilnya. Jika tidak,bdievaluasi dan menjadi hasilnya. - Jika tidak,
adanbtidak 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
Tuntuk metodeMtidak dibatasi. Oleh karena itu, argumen jenis dapat berupa jenis referensi, atau jenis nilai nullable, seperti yang ditunjukkan dalam panggilan pertama keM. Argumen jenis juga dapat berupa jenis nilai yang tidak dapat diubah ke null, seperti yang ditunjukkan dalam panggilan kedua keM. Ketika argumen jenis adalah jenis nilai yang tidak dapat diubah ke null, nilai ekspresia ?? badalaha.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
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
s1menunjukkan ekspresi deklarasi yang diketik secara eksplisit dan implisit. Jenis yang disimpulkan darib1adalahboolkarena ini adalah jenis parameter output yang sesuai dalamM1.WriteLineberikutnya dapat mengaksesi1danb1, yang telah diperkenalkan dalam ruang lingkup tercakup.Deklarasi
s2menunjukkan upaya untuk menggunakani2dalam panggilan berlapis keM, yang tidak diizinkan, karena referensi terjadi dalam daftar argumen tempati2dideklarasikan. Di sisi lain referensi keb2dalam argumen akhir diizinkan, karena terjadi setelah akhir daftar argumen berlapis tempatb2dideklarasikan.Deklarasi
s3menunjukkan 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 denganvar _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 : edievaluasi sebagaia ? 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 memilihdynamic(§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
refada, conditional_expression mengembalikan referensi variabel, yang dapat ditetapkan ke variabel referensi menggunakan operator= refatau 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
xmemiliki jenisXdanymemiliki jenisY,- Jika konversi identitas ada antara
XdanY, hasilnya adalah jenis ekspresi umum terbaik (§12.6.3.16). Jika salah satu jenis adalahdynamic, inferensi tipe lebih memilihdynamic(§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
XkeY, tetapi tidak dariYkeX, makaYadalah jenis ekspresi bersyarkat. - Jika tidak, jika konversi enumerasi implisit (§10,2,4) ada dari
XkeY, makaYadalah jenis ekspresi kondisional. - Jika tidak, jika konversi enumerasi implisit (§10,2,4) ada dari
YkeX, makaXadalah jenis ekspresi kondisional. - Jika tidak, jika konversi implisit (§10,2) ada dari
YkeX, tetapi tidak dariXkeY, makaXadalah jenis ekspresi bersyarkat. - Jika tidak, tidak ada jenis ekspresi yang dapat ditentukan, dan terjadi kesalahan waktu kompilasi.
- Jika konversi identitas ada antara
- Jika hanya salah satu
xdanyyang memiliki jenis, danxdanysecara 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,
bdievaluasi, dan nilaiboolbditentukan:- Jika konversi implisit dari jenis
bkeboolada, konversi implisit ini dilakukan untuk menghasilkan nilaibool. - Jika tidak,
operator trueyang ditentukan oleh jenisbdipanggil untuk menghasilkan nilaibool.
- Jika konversi implisit dari jenis
- Jika nilai
boolyang dihasilkan oleh langkah di atas adalahtrue, makaxakan dievaluasi dan referensi variabel yang dihasilkan akan menjadi hasil dari ekspresi bersyarat. - Jika tidak,
ydievaluasi dan referensi variabel yang dihasilkan menjadi hasil ekspresi kondisional.
Pemrosesan run-time ekspresi kondisional formulir b ? x : y terdiri dari langkah-langkah berikut:
- Pertama,
bdievaluasi, dan nilaiboolbditentukan:- Jika konversi implisit dari jenis
bkeboolada, konversi implisit ini dilakukan untuk menghasilkan nilaibool. - Jika tidak,
operator trueyang ditentukan oleh jenisbdipanggil untuk menghasilkan nilaibool.
- Jika konversi implisit dari jenis
- Jika nilai
boolyang dihasilkan oleh langkah di atastrue, makaxdievaluasi dan dikonversi ke jenis ekspresi kondisional, dan ini menjadi hasil ekspresi kondisional. - Jika tidak,
ydievaluasi 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 dariMadalahvoid(§12.8.13). Tetapi ketika diperlakukan sebagai null_conditional_invocation_expression, tipe hasil bolehvoid. catatan akhir
Contoh: Jenis hasil dari
List<T>.Reverseadalahvoid. 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 omittedcontoh 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
thisadalah tipe struktur, terjadinya kesalahan pada waktu kompilasi jika bagian tersebut mencoba mengaksesthis. Ini benar apakah aksesnya eksplisit (seperti dalamthis.x) atau implisit (seperti padaxdi manaxadalah 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, pernyataanbreak, atau pernyataancontinueyang targetnya berada di luar badan atau berada dalam badan fungsi anonim yang terkandung. - Pernyataan
returndi 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 metodeSum. Masing-masing mengambil sebuah argumenselector, yang mengekstrak nilai untuk dijumlahkan dari setiap item dalam daftar. Nilai yang diekstrak dapat berupaintataudoubledan jumlah yang dihasilkan juga merupakanintataudouble.Metode
Summisalnya 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 metodeSumberlaku karena fungsi anonimd => d.UnitCountkompatibel denganFunc<Detail,int>danFunc<Detail,double>. Namun, resolusi kelebihan beban memilih metodeSumpertama karena konversi keFunc<Detail,int>lebih baik daripada konversi keFunc<Detail,double>.Dalam pemanggilan kedua
orderDetails.Sum, hanya metodeSumkedua yang berlaku karena fungsi anonimd => d.UnitPrice * d.UnitCountmenghasilkan nilai jenisdouble. Dengan demikian, resolusi kelebihan beban memilih metodeSumkedua 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
xditangkap oleh fungsi anonim, dan masa pakaixdiperpanjang setidaknya sampai delegasi yang dikembalikan dariFmenjadi memenuhi syarat untuk pengumpulan sampah. Karena setiap pemanggilan fungsi anonim beroperasi pada instansxyang sama , output contohnya adalah:1 2 3contoh 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
xdibuat 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
xdipindahkan ke luar perulangan, itu akan menghasilkan satu instansiasix: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 5Namun, ketika deklarasi
xdipindahkan 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 5Perhatikan 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 3contoh akhir
Dimungkinkan bagi delegasi fungsi anonim untuk berbagi beberapa variabel yang ditangkap tetapi memiliki instans terpisah dari yang lain.
Contoh: Misalnya, jika
Fdiubah menjadistatic 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
xyang sama namun instans terpisah dariy, dan outputnya adalah:1 1 2 1 3 1contoh 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 10contoh 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 cditerjemahkan ke dalam
from c in (customers).Cast<Customer>() where c.City == "London" select cterjemahan akhirnya adalah
customers. Cast<Customer>(). Where(c => c.City == "London")contoh akhir
Catatan: Jenis variabel rentang eksplisit berguna untuk mengkueri koleksi yang mengimplementasikan antarmuka
IEnumerablenon-generik, tetapi bukan antarmukaIEnumerable<T>generik. Dalam contoh di atas, ini akan terjadi jika pelanggan berjenisArrayList. 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 cditerjemahkan 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
Selectpada sumbernya. Kemudian terserah pelaksanaSelectdan 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
xadalah 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
xadalah 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
xdanyadalah 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 omemiliki 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 chanya 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.Countryditerjemahkan 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
xadalah 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
xdanyadalah 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>danO<T>yang memastikan bahwa metodeThenBydanThenByDescendinghanya tersedia pada hasilOrderByatauOrderByDescending. catatan akhir
Catatan: Bentuk hasil yang direkomendasikan dari
GroupBy—serangkaian urutan, di mana setiap urutan memiliki propertiKeytambahan. 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.Linqmenyediakan implementasi pola ekspresi kueri untuk jenis apa pun yang mengimplementasikan antarmukaSystem.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 adalahnull, 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 = cdievaluasi sebagaia = (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
xadalah ekspresi tuple(x1, ..., xn), danydapat didekonstruksi ke ekspresi tuple(y1, ..., yn)dengan elemenn(§12,7), dan setiap penugasan untukxiyimemiliki jenisTi, maka tugas memiliki jenis(T1, ..., Tn). - Jika tidak, jika
xdiklasifikasikan sebagai variabel, variabel tidakreadonly,xmemiliki jenisT, danymemiliki konversi implisit keT, maka pengisian nilai memiliki jenisT. - Jika tidak, jika
xdiklasifikasikan sebagai variabel yang diketik secara implisit (yaitu ekspresi deklarasi yang diketik secara implisit) danymemiliki jenisT, maka jenis variabel yang disimpulkanT, dan penugasan memiliki jenisT. - Selain itu, jika
xdiklasifikasikan sebagai akses properti atau pengindeks, dan properti atau pengindeks tersebut memiliki aksesor set yang dapat diakses,xmemiliki jenisT, sertaymemiliki konversi implisit keT, maka pengisian tersebut memiliki jenisT. - 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:
-
xdievaluasi jika belum dievaluasi sebelumnya. - Jika
xdiklasifikasikan sebagai variabel,ydievaluasi dan, jika diperlukan, dikonversi keTmelalui konversi implisit (§10,2).- Jika variabel yang diberikan oleh
xadalah elemen array dari reference_type, pemeriksaan waktu jalan dilakukan untuk memastikan bahwa nilai yang dihitung untukykompatibel dengan instance array tersebut yang merupakan elemenx. Pemeriksaan berhasil jikaynull, atau jika konversi referensi implisit (§10,2,8) ada dari jenis instans yang dirujuk olehyke jenis elemen aktual dari instans array yang berisix. Jika tidak,System.ArrayTypeMismatchExceptionakan dilempar. - Nilai yang dihasilkan dari evaluasi dan konversi
ydisimpan ke lokasi yang diberikan oleh evaluasix, dan dihasilkan sebagai hasil dari pengalokasian.
- Jika variabel yang diberikan oleh
- Jika
xdiklasifikasikan sebagai akses properti atau pengindeks:-
ydievaluasi dan, jika diperlukan, dikonversi keTmelalui konversi implisit (§10,2). - Aksesor set
xdipanggil dengan nilai hasil evaluasi dan konversi dariysebagai argumen nilainya. - Nilai yang dihasilkan dari evaluasi dan konversi
ydihasilkan sebagai hasil dari penugasan.
-
- Jika
xdiklasifikasikan sebagai tuple(x1, ..., xn)dengan aritasn:-
ydidekonstruksi menggunakan elemennmenjadi ekspresi tuplee. -
ttuple hasil dibuat dengan mengonversiemenjadiTmelalui konversi tuple implisit. - untuk setiap
xisecara berurutan dari kiri ke kanan, penugasan kexidarit.Itemidilakukan, kecuali bahwaxitidak dievaluasi lagi. -
tdihasilkan sebagai hasil dari penugasan.
-
Catatan: jika jenis waktu kompilasi
xadalahdynamicdan ada konversi implisit dari jenis waktu kompilasiykedynamic, tidak diperlukan resolusi runtime. catatan akhir
Catatan: Aturan kovarians array (§17.6) mengizinkan nilai tipe array
A[]sebagai referensi ke sebuah instans tipe arrayB[], dengan syarat terdapat konversi referensi implisit dariBkeA. Karena aturan ini, pengisian ke elemen array reference_type memerlukan pemeriksaan saat runtime untuk memastikan bahwa nilai yang diberikan kompatibel dengan instance array. Dalam contohstring[] sa = new string[10]; object[] oa = sa; oa[0] = null; // OK oa[1] = "Hello"; // OK oa[2] = new ArrayList(); // ArrayTypeMismatchExceptionpenugasan terakhir menyebabkan
System.ArrayTypeMismatchExceptiondilemparkan karena referensi keArrayListtidak dapat disimpan dalam elemenstring[].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, danr.Bdiizinkan karenapdanradalah variabel. Namun, dalam contohRectangle 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.Adanr.Bbukan 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 bagianrefsebagai bagian dari operand. Ini sangat membingungkan ketika operand adalah ekspresi kondisional?:. Misalnya, saat membacaref int a = ref b ? ref x : ref y;penting untuk membaca ini karena= refmenjadi operator, danb ? ref x : ref ymenjadi operan yang tepat:ref int a = ref (b ? ref x : ref y);. Yang penting, ekspresiref 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 sebagaix = x «op» y, kecuali bahwaxdievaluasi 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 jikaysecara implisit dapat dikonversi ke jenisxatau operator adalah operator shift, maka operasi dievaluasi sebagaix = (T)(x «op» y), di manaTadalah jenisx, kecuali bahwaxdievaluasi 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 manaAadalah metode yang mengembalikanint[], danBdanCadalah metode yang mengembalikanint, metode ini hanya dipanggil satu kali sesuai urutanA,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; // OKalasan 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»= ydievaluasi sebagaix = x «op» yataux = (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
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
constanggota 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
checkeddanunchecked. -
nameofekspresi. - 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). -
sizeofekspresi, asalkan jenis yang tidak dikelola adalah salah satu jenis yang ditentukan dalam §24.6.9 yangsizeofmengembalikan 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-
nulltidak 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
iadalah kesalahan karena konversi tinju diperlukan. Inisialisasistrdianggap 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)
-
caselabel dari pernyataanswitch(§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
boolmaka pada run-time konversi implisit diterapkan. - Jika tidak, resolusi kelebihan beban operator unary (§12.4.4) digunakan untuk menemukan implementasi terbaik unik
operator truepadaE, dan implementasi tersebut diterapkan pada waktu run-time. - Jika operator tersebut tidak ditemukan, kesalahan waktu pengikatan terjadi.
ECMA C# draft specification