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.
Nota
Artikel ini adalah spesifikasi fitur. Spesifikasi berfungsi sebagai dokumen desain untuk fitur tersebut. Ini termasuk perubahan spesifikasi yang diusulkan, bersama dengan informasi yang diperlukan selama desain dan pengembangan fitur. Artikel ini diterbitkan sampai perubahan spesifikasi yang diusulkan diselesaikan dan dimasukkan dalam spesifikasi ECMA saat ini.
Mungkin ada beberapa perbedaan antara spesifikasi fitur dan implementasi yang selesai. Perbedaan tersebut dicatat dalam catatan terkait rapat desain bahasa (LDM) .
Anda dapat mempelajari lebih lanjut tentang proses untuk mengadopsi speklet fitur ke dalam standar bahasa C# dalam artikel tentang spesifikasi .
Masalah juara: https://github.com/dotnet/csharplang/issues/45
Ringkasan
Ekstensi pencocokan pola untuk C# memungkinkan banyak manfaat dari jenis data aljabar dan pencocokan pola dari bahasa fungsional, namun secara mulus terintegrasi dengan karakteristik khas bahasa dasarnya. Elemen pendekatan ini terinspirasi oleh fitur terkait dalam bahasa pemrograman F# dan Scala.
Desain terperinci
Adalah Ekspresi
Operator is
diperluas untuk menguji ekspresi terhadap pola .
relational_expression
: is_pattern_expression
;
is_pattern_expression
: relational_expression 'is' pattern
;
Bentuk relational_expression ini merupakan tambahan pada bentuk yang ada dalam spesifikasi C#. Ini adalah kesalahan waktu kompilasi jika relational_expression di sebelah kiri token is
tidak menunjuk nilai atau tidak memiliki jenis.
Setiap pengidentifikasi pola memperkenalkan variabel lokal baru yang dengan pasti ditetapkan setelah operator is
true
(yaitu dengan pasti ditetapkan ketika benar ).
Catatan: Secara teknis ada ambiguitas antara jenis dalam
is-expression
dan constant_pattern, yang salah satunya mungkin merupakan penguraian yang valid dari pengidentifikasi yang memenuhi syarat. Kami mencoba mengikatnya sebagai jenis untuk kompatibilitas dengan versi bahasa sebelumnya; hanya jika itu gagal, kami menyelesaikannya seperti yang kami lakukan dengan ekspresi dalam konteks lain, ke hal pertama yang kami temukan (yang harus berupa konstanta atau jenis). Ambiguitas ini hanya ada di sisi kanan ekspresiis
.
Pola
Pola digunakan dalam operator is_pattern, dalam switch_statement, dan dalam switch_expression untuk mengekspresikan bentuk data tempat data masuk (yang kami sebut nilai input) akan dibandingkan. Pola mungkin rekursif sehingga bagian data dapat dicocokkan dengan sub-pola.
pattern
: declaration_pattern
| constant_pattern
| var_pattern
| positional_pattern
| property_pattern
| discard_pattern
;
declaration_pattern
: type simple_designation
;
constant_pattern
: constant_expression
;
var_pattern
: 'var' designation
;
positional_pattern
: type? '(' subpatterns? ')' property_subpattern? simple_designation?
;
subpatterns
: subpattern
| subpattern ',' subpatterns
;
subpattern
: pattern
| identifier ':' pattern
;
property_subpattern
: '{' '}'
| '{' subpatterns ','? '}'
;
property_pattern
: type? property_subpattern simple_designation?
;
simple_designation
: single_variable_designation
| discard_designation
;
discard_pattern
: '_'
;
Pola Deklarasi
declaration_pattern
: type simple_designation
;
declaration_pattern menguji apakah ekspresi termasuk dalam jenis tertentu dan mengonversinya menjadi jenis tersebut jika pengujian berhasil. Ini dapat memperkenalkan variabel lokal dari jenis yang diberikan, yang diberi nama sesuai pengidentifikasi yang diberikan, jika penandaannya adalah berupa single_variable_designation. Variabel lokal tersebut memang diberikan ketika hasil dari operasi pencocokan pola adalah true
.
Semantik runtime ekspresi ini adalah menguji jenis runtime operan null
, hasil dari is operator
adalah true
.
Kombinasi tertentu dari tipe statis pada sisi kiri dan tipe yang diberikan dianggap tidak kompatibel dan mengakibatkan kesalahan saat waktu kompilasi. Nilai jenis statis E
dikatakan yang kompatibel dengan pola dengan jenis T
jika ada konversi identitas, konversi referensi implisit, konversi tinju, konversi referensi eksplisit, atau konversi pembukaan kotak dari E
ke T
, atau jika salah satu jenis tersebut adalah jenis terbuka. Ini adalah kesalahan waktu kompilasi jika input jenis E
tidak kompatibel pola dengan tipe dalam pola jenis yang dicocokkan dengannya.
Pola tipe berguna untuk melakukan pengujian tipe run-time dari tipe referensi, dan menggantikan idiom.
var v = expr as Type;
if (v != null) { // code using v
Dengan sedikit lebih ringkas
if (expr is Type v) { // code using v
Ini adalah kesalahan jika jenis adalah jenis nilai nullable.
Pola jenis dapat digunakan untuk menguji nilai jenis nullable: nilai jenis Nullable<T>
(atau T
kotak ) cocok dengan pola jenis T2 id
jika nilainya tidak null dan jenis T2
T
, atau beberapa jenis dasar atau antarmuka T
. Misalnya, dalam fragmen kode
int? x = 3;
if (x is int v) { // code using v
Pada runtime, kondisi pernyataan if
adalah true
, dan variabel v
menyimpan nilai 3
dengan tipe int
dalam blok. Setelah blok, variabel v
berada dalam cakupan tetapi tidak pasti ditetapkan.
Pola Konstanta
constant_pattern
: constant_expression
;
Pola konstan menguji nilai ekspresi terhadap nilai konstanta. Konstanta mungkin ekspresi konstanta apa pun, seperti harfiah, nama variabel const
yang dideklarasikan, atau konstanta enumerasi. Ketika nilai input bukan jenis terbuka, ekspresi konstanta secara implisit dikonversi ke jenis ekspresi yang cocok; jika jenis nilai input tidak yang kompatibel dengan pola dengan jenis ekspresi konstanta, operasi pencocokan pola adalah kesalahan.
Pola c dianggap cocok dengan nilai input yang dikonversi e jika object.Equals(c, e)
akan mengembalikan true
.
Kami berharap untuk melihat e is null
sebagai cara paling umum untuk menguji null
dalam kode yang baru ditulis, karena ini tidak dapat memanggil operator==
yang ditentukan pengguna.
Pola Var
var_pattern
: 'var' designation
;
designation
: simple_designation
| tuple_designation
;
simple_designation
: single_variable_designation
| discard_designation
;
single_variable_designation
: identifier
;
discard_designation
: _
;
tuple_designation
: '(' designations? ')'
;
designations
: designation
| designations ',' designation
;
Jika penunjukan adalah penunjukan_sederhana , ekspresi e cocok dengan pola. Dengan kata lain, mencocokkan pola var selalu berhasil menggunakan penunjukan sederhana . Jika simple_designation adalah single_variable_designation, nilai e terikat ke variabel lokal yang baru diperkenalkan. Jenis variabel lokal adalah jenis statis e.
Jika penandaan var (x, (y, z))
setara dengan (var x, (var y, var z))
.
Ini adalah kesalahan jika nama var
melekat pada tipe.
Buang Pola
discard_pattern
: '_'
;
Ekspresi e selalu cocok dengan pola _
. Dengan kata lain, setiap ekspresi cocok dengan pola pengabaian.
Pola pembuangan mungkin tidak dapat digunakan sebagai pola dari suatu is_pattern_expression.
Pola Posisi
Pola posisional memeriksa bahwa nilai input tidak null
, memanggil metode Deconstruct
yang sesuai, dan melakukan pencocokan pola lebih lanjut pada nilai yang dihasilkan. Ini juga mendukung sintaks pola seperti tuple (tanpa jenis yang disediakan) ketika tipe nilai input sama dengan tipe yang mengandung Deconstruct
, atau jika tipe nilai input adalah tipe tuple, atau jika tipe nilai input adalah object
atau ITuple
dan tipe runtime dari ekspresi tersebut mengimplementasikan ITuple
.
positional_pattern
: type? '(' subpatterns? ')' property_subpattern? simple_designation?
;
subpatterns
: subpattern
| subpattern ',' subpatterns
;
subpattern
: pattern
| identifier ':' pattern
;
Jika jenis dihilangkan, kami menganggapnya sebagai jenis statis dari nilai input.
Mengingat kecocokan nilai input dengan pola jenis(
subpattern_list)
, metode dipilih dengan mencari dalam jenis untuk deklarasi Deconstruct
yang dapat diakses dan memilih salah satunya menggunakan aturan yang sama seperti untuk deklarasi dekonstruksi.
Ini adalah kesalahan jika
Untuk mengekstrak nilai yang cocok dengan pola dalam daftar,
- Jika jenis dihilangkan dan tipe nilai input adalah tipe tuple, maka jumlah subpola harus sama dengan kardinalitas tuple. Setiap elemen tuple dicocokkan dengan subpatternyang sesuai, dan kecocokan berhasil jika semua ini berhasil. Jika ada subpola memiliki pengenal , maka itu harus memberi nama elemen tuple pada posisi yang sesuai dalam tipe tuple.
- Jika tidak, jika
yang sesuai ada sebagai anggotajenis subpattern, itu adalah kesalahan waktu kompilasi jika jenis nilai input tidak yang kompatibel dengan pola dengan jenis . Pada saat waktu eksekusi, nilai input diuji terhadap jenis . Jika ini gagal, maka kecocokan pola posisi gagal. Jika berhasil, nilai input dikonversi ke jenis ini dan Deconstruct
dipanggil dengan variabel baru yang dihasilkan kompilator untuk menerima parameterout
. Setiap nilai yang diterima dicocokkan denganyang sesuai , dan kecocokan berhasil jika semua ini berhasil. Jika subpola memiliki identifier , maka itu harus menamai parameter pada posisi Deconstruct
yang sesuai. - Jika jenis dihilangkan, dan nilai input berjenis
object
atauITuple
atau beberapa jenis yang dapat dikonversi keITuple
melalui konversi referensi implisit, serta tidak ada pengidentifikasi yang muncul di antara subpola, maka kita mencocokkan menggunakanITuple
. - Jika tidak, pola tersebut merupakan kesalahan waktu kompilasi.
Urutan di mana subpola dicocokkan saat waktu proses tidak ditentukan, dan kecocokan yang gagal mungkin tidak mencoba mencocokkan semua subpola.
Contoh
Contoh ini menggunakan banyak fitur yang dijelaskan dalam spesifikasi ini
var newState = (GetState(), action, hasKey) switch {
(DoorState.Closed, Action.Open, _) => DoorState.Opened,
(DoorState.Opened, Action.Close, _) => DoorState.Closed,
(DoorState.Closed, Action.Lock, true) => DoorState.Locked,
(DoorState.Locked, Action.Unlock, true) => DoorState.Closed,
(var state, _, _) => state };
Pola Properti
Pola properti memeriksa bahwa nilai input tidak null
dan secara rekursif cocok dengan nilai yang diekstrak oleh penggunaan properti atau bidang yang dapat diakses.
property_pattern
: type? property_subpattern simple_designation?
;
property_subpattern
: '{' '}'
| '{' subpatterns ','? '}'
;
Ini adalah kesalahan jika subpattern dari property_pattern tidak berisi pengidentifikasi (yang harus berupa bentuk kedua yang memiliki pengidentifikasi). Koma berikutnya setelah subpattern terakhir bersifat opsional.
Perhatikan bahwa pola pemeriksaan null keluar dari pola properti sepele. Untuk memeriksa apakah string s
non-null, Anda dapat menulis salah satu formulir berikut
if (s is object o) ... // o is of type object
if (s is string x) ... // x is of type string
if (s is {} x) ... // x is of type string
if (s is {}) ...
Mengingat kecocokan ekspresi
Pada runtime, ekspresi diuji terhadap T. Jika ini gagal, maka kecocokan pola properti gagal dan hasilnya false
. Jika berhasil, maka setiap field atau properti property_subpattern dibaca dan nilainya dicocokkan dengan pola yang sesuai. Hasil dari seluruh pertandingan adalah false
hanya jika hasil dari salah satu dari ini adalah false
. Urutan di mana subpattern dicocokkan tidak ditentukan, dan kecocokan yang gagal mungkin tidak cocok dengan semua subpattern saat runtime. Jika kecocokan berhasil dan simple_designation dari property_pattern adalah single_variable_designation, maka itu mendefinisikan sebuah variabel dengan jenis T yang ditugaskan nilai yang cocok.
Catatan: Pola properti dapat digunakan untuk mencocokkan pola dengan jenis tanpa nama.
Contoh
if (o is string { Length: 5 } s)
Beralih Ekspresi
switch_expression ditambahkan untuk mendukung semantik yang mirip switch
dalam konteks ekspresi.
Sintaks bahasa C# ditambah dengan produksi sintaktik berikut:
multiplicative_expression
: switch_expression
| multiplicative_expression '*' switch_expression
| multiplicative_expression '/' switch_expression
| multiplicative_expression '%' switch_expression
;
switch_expression
: range_expression 'switch' '{' '}'
| range_expression 'switch' '{' switch_expression_arms ','? '}'
;
switch_expression_arms
: switch_expression_arm
| switch_expression_arms ',' switch_expression_arm
;
switch_expression_arm
: pattern case_guard? '=>' expression
;
case_guard
: 'when' null_coalescing_expression
;
switch_expression tidak diizinkan sebagai expression_statement.
Kami sedang mempertimbangkan untuk melonggarkan hal ini dalam revisi di masa depan.
Jenis T
di mana terdapat konversi implisit dari ekspresi setiap arm ke T
.
Ini adalah kesalahan jika beberapa pola switch_expression_armtidak dapat memengaruhi hasil karena beberapa pola dan penjaga sebelumnya akan selalu cocok.
Ekspresi pengalihan dikatakan lengkap jika beberapa lengan ekspresi pengalihan menangani setiap nilai inputnya. Pengkompilasi akan menghasilkan peringatan jika ekspresi pengalihan tidak lengkap .
Saat runtime, hasil dari switch_expression adalah nilai ekspresi dari switch_expression_arm pertama di mana ekspresi di sisi kiri switch_expression cocok dengan pola switch_expression_arm, serta jika ada case_guard pada switch_expression_arm, dieksekusi sebagai true
. Jika tidak ada switch_expression_armyang dimaksud, switch_expression melempar objek pengecualian System.Runtime.CompilerServices.SwitchExpressionException
.
Kurung opsional ketika melakukan switch pada literal tuple
Untuk mengaktifkan tuple literal menggunakan switch_statement, Anda harus menulis apa yang tampaknya menjadi paren redundan
switch ((a, b))
{
Untuk mengizinkan
switch (a, b)
{
tanda kurung dalam pernyataan switch bersifat opsional ketika ekspresi yang digunakan adalah tuple literal.
Urutan evaluasi dalam pencocokan pola
Memberikan fleksibilitas kompilator dalam menyusun ulang operasi yang dijalankan selama pencocokan pola dapat memungkinkan fleksibilitas yang dapat digunakan untuk meningkatkan efisiensi pencocokan pola. Persyaratan (tidak diberlakukan) adalah bahwa properti yang diakses dalam pola, dan metode Dekonstruksi, wajib "tanpa efek samping" (bebas efek samping, idempoten, dll). Itu tidak berarti bahwa kita akan menambahkan kemurnian sebagai konsep bahasa, hanya saja kita akan memungkinkan fleksibilitas kompilator dalam menyusun ulang operasi.
Resolusi 2018-04-04 LDM: dikonfirmasi: kompiler diizinkan untuk mengubah urutan panggilan ke Deconstruct
, pengaksesan properti, dan pemanggilan metode dalam ITuple
, dan dapat mengasumsikan bahwa nilai yang dihasilkan sama dari beberapa panggilan. Pengkompilasi tidak boleh memanggil fungsi yang tidak dapat memengaruhi hasilnya, dan kami akan sangat berhati-hati sebelum membuat perubahan pada urutan evaluasi yang dihasilkan kompilator di masa mendatang.
Beberapa Kemungkinan Pengoptimalan
Kompilasi pencocokan pola dapat memanfaatkan bagian pola umum. Misalnya, jika pengujian jenis tingkat atas dari dua pola berturut-turut dalam switch_statement adalah jenis yang sama, kode yang dihasilkan dapat melewati pengujian jenis untuk pola kedua.
Ketika beberapa pola adalah bilangan bulat atau string, pengkompilasi dapat menghasilkan jenis kode yang sama yang dihasilkannya untuk pernyataan pengalihan dalam versi bahasa yang lebih lama.
Untuk informasi selengkapnya tentang pengoptimalan semacam ini, lihat [Scott dan Ramsey (2000)].
C# feature specifications