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 .
Kami sedang mempertimbangkan beberapa penyempurnaan kecil untuk pencocokan pola untuk C# 9.0 yang memiliki sinergi alami dan bekerja dengan baik untuk mengatasi sejumlah masalah pemrograman umum:
- Pola jenis https://github.com/dotnet/csharplang/issues/2925
- https://github.com/dotnet/csharplang/issues/1350 pola di dalam tanda kurung untuk memperkuat atau menekankan prioritas kombinator baru
-
https://github.com/dotnet/csharplang/issues/1350 Pola
and
konjugtif yang mengharuskan kedua pola berbeda untuk dicocokkan; -
https://github.com/dotnet/csharplang/issues/1350 Pola
or
disjungtif yang mengharuskan salah satu dari dua pola yang berbeda untuk dicocokkan; -
https://github.com/dotnet/csharplang/issues/1350 Pola
not
yang dinegasikan yang memerlukan agar pola tertentu tidak cocok; dan - https://github.com/dotnet/csharplang/issues/812 Pola relasional yang mengharuskan nilai input kurang dari, kurang dari atau sama dengan, dll konstanta tertentu.
Pola Yang Dikurung
Pola yang dikurung memungkinkan programmer menempatkan tanda kurung di sekitar pola apa pun. Ini tidak begitu berguna dengan pola yang ada di C# 8.0, namun kombinator pola baru memperkenalkan prioritas yang mungkin ingin mengesampingkan oleh programmer.
primary_pattern
: parenthesized_pattern
| // all of the existing forms
;
parenthesized_pattern
: '(' pattern ')'
;
Pola Jenis
Kami mengizinkan jenis sebagai pola:
primary_pattern
: type-pattern
| // all of the existing forms
;
type_pattern
: type
;
Ini mengubah secara retroaktif is-type-expression yang ada menjadi bentuk is-pattern-expression di mana polanya adalah pola jenis , meskipun pohon sintaks yang dihasilkan oleh kompilator tetap tidak berubah.
Salah satu masalah implementasi yang samar adalah bahwa tata bahasa ini ambigu. Sebuah string seperti a.b
dapat diurai baik sebagai nama terkelola (dalam konteks tipe) atau ekspresi ber-dot (dalam konteks ekspresi). Pengkompilasi sudah mampu memperlakukan nama berkualifikasi sama dengan ekspresi bertitik untuk menangani sesuatu seperti e is Color.Red
. Analisis semantik kompilator akan diperluas lebih lanjut agar dapat mengikat pola konstanta (sintaktik) (seperti dalam ekspresi bertitik) sebagai tipe demi memperlakukannya sebagai pola tipe terikat untuk mendukung konstruksi ini.
Setelah perubahan ini, Anda akan dapat menulis
void M(object o1, object o2)
{
var t = (o1, o2);
if (t is (int, string)) {} // test if o1 is an int and o2 is a string
switch (o1) {
case int: break; // test if o1 is an int
case System.String: break; // test if o1 is a string
}
}
Pola Relasional
Pola relasional memungkinkan programmer untuk mengekspresikan bahwa nilai input harus memenuhi batasan relasional jika dibandingkan dengan nilai konstanta:
public static LifeStage LifeStageAtAge(int age) => age switch
{
< 0 => LifeStage.Prenatal,
< 2 => LifeStage.Infant,
< 4 => LifeStage.Toddler,
< 6 => LifeStage.EarlyChild,
< 12 => LifeStage.MiddleChild,
< 20 => LifeStage.Adolescent,
< 40 => LifeStage.EarlyAdult,
< 65 => LifeStage.MiddleAdult,
_ => LifeStage.LateAdult,
};
Pola relasional mendukung operator relasional <
, <=
, >
, dan >=
pada semua jenis bawaan yang mendukung operator relasional biner tersebut dengan dua operan dengan jenis yang sama dalam ekspresi. Secara khusus, kami mendukung semua pola relasional ini untuk sbyte
, byte
, short
, ushort
, int
, uint
, long
, ulong
, char
, float
, double
, decimal
, nint
, dan nuint
.
primary_pattern
: relational_pattern
;
relational_pattern
: '<' relational_expression
| '<=' relational_expression
| '>' relational_expression
| '>=' relational_expression
;
Ekspresi diperlukan untuk mengevaluasi ke nilai konstanta. Ini adalah kesalahan jika nilai konstanta tersebut double.NaN
atau float.NaN
. Ini adalah kesalahan jika ekspresi adalah konstanta null.
Ketika input adalah jenis untuk mana operator relasional biner bawaan yang sesuai didefinisikan dan dapat diterapkan dengan input sebagai operan kiri dan konstanta yang diberikan sebagai operan kanan, maka evaluasi operator tersebut diambil sebagai makna dari pola relasional. Jika tidak, kami mengonversi input ke tipe ekspresi menggunakan konversi nullable atau unboxing yang eksplisit. Ini adalah kesalahan waktu kompilasi jika tidak ada konversi tersebut. Pola dianggap tidak cocok jika konversi gagal. Jika konversi berhasil, hasil dari operasi pencocokan pola adalah hasil dari mengevaluasi ekspresi e OP v
di mana e
adalah input yang dikonversi, OP
adalah operator relasional, dan v
adalah ekspresi konstan.
Pola Kombinator
Pola kombinator
Penggunaan umum combinator adalah idiom
if (e is not null) ...
Lebih mudah dibaca daripada idiom saat ini e is object
, pola ini dengan jelas mengekspresikan bahwa seseorang sedang memeriksa nilai non-null.
Combinator and
dan or
akan berguna untuk menguji rentang nilai
bool IsLetter(char c) => c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';
Contoh ini menggambarkan bahwa and
akan memiliki prioritas penguraian yang lebih tinggi (yaitu akan mengikat lebih dekat) daripada or
. Programmer dapat menggunakan pola kurung untuk membuat prioritas eksplisit:
bool IsLetter(char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');
Seperti semua pola, combinator ini dapat digunakan dalam konteks apa pun di mana pola diharapkan, termasuk pola berlapis, is-pattern-expression, switch-expression, dan pola label kasus pernyataan pengalihan.
pattern
: disjunctive_pattern
;
disjunctive_pattern
: disjunctive_pattern 'or' conjunctive_pattern
| conjunctive_pattern
;
conjunctive_pattern
: conjunctive_pattern 'and' negated_pattern
| negated_pattern
;
negated_pattern
: 'not' negated_pattern
| primary_pattern
;
primary_pattern
: // all of the patterns forms previously defined
;
Ganti ke 6.2.5 Ambiguitas dalam Tata Bahasa
Karena pengenalan pola tipe , ada kemungkinan tipe generik muncul sebelum token =>
. Oleh karena itu, kami menambahkan =>
ke kumpulan token yang tercantum dalam bagian ยง6.2.5 mengenai ambiguitas tata bahasa untuk mengizinkan disambiguasi dari <
yang mengawali daftar argumen jenis. Lihat juga https://github.com/dotnet/roslyn/issues/47614.
Masalah Terbuka dengan Perubahan yang Diusulkan
Sintaks untuk operator relasional
Apakah and
, or
, dan not
semacam kata kunci kontekstual? Jika demikian, apakah ada perubahan yang signifikan (misalnya dibandingkan dengan penggunaannya sebagai penunjuk dalam pola deklarasi).
Semantik (misalnya jenis) untuk operator relasional
Kami berharap dapat mendukung semua jenis primitif yang dapat dibandingkan dalam ekspresi menggunakan operator relasional. Arti dalam kasus sederhana jelas
bool IsValidPercentage(int x) => x is >= 0 and <= 100;
Tetapi ketika input bukan jenis primitif, ke jenis apa kita mencoba mengkonversinya?
bool IsValidPercentage(object x) => x is >= 0 and <= 100;
Kami telah mengusulkan bahwa ketika tipe input sudah berupa primitif yang dapat dibandingkan, itulah tipe untuk perbandingan. Namun, ketika input bukan primitif yang sebanding, kami memperlakukan relasional bagaikan mencakup pengujian tipe implisit terhadap tipe konstanta di sisi kanan relasional. Jika programmer ingin mendukung lebih dari satu jenis input, itu harus dilakukan secara eksplisit:
bool IsValidPercentage(object x) => x is
>= 0 and <= 100 or // integer tests
>= 0F and <= 100F or // float tests
>= 0D and <= 100D; // double tests
Hasil: Tipe relasional memang menyertakan pengujian tipe implisit terhadap tipe konstanta di sisi kanan dari relasional tersebut.
Aliran informasi tipe dari kiri ke kanan and
Telah disarankan bahwa ketika Anda menulis kombinator and
, informasi jenis yang dipelajari di sebelah kiri tentang jenis tingkat atas tersebut dapat mengalir ke sebelah kanan. Misalnya
bool isSmallByte(object o) => o is byte and < 100;
Di sini, jenis input P
didefinisikan sebagai berikut:
- Jika
P
adalah pola jenis, jenis yang dipersempit adalah jenis dari pola jenis tersebut. - Jika
P
adalah pola deklarasi, maka tipe yang dipersempit menjadi adalah tipe dari pola deklarasi tersebut. - Jika
P
adalah pola rekursif yang memberikan jenis eksplisit, jenis dipersempit adalah jenis tersebut. - Jika
P
sesuai dengan aturan untukITuple
, jenis yang dipersempit menjadi adalah jenisSystem.Runtime.CompilerServices.ITuple
. - Jika
P
adalah pola konstanta di mana konstanta bukan konstanta null dan di mana ekspresi tidak memiliki konversi ekspresi konstanta ke jenis input , maka jenis yang dipersempit adalah jenis dari konstanta tersebut. - Jika
P
adalah pola relasional di mana ekspresi konstanta tidak mengalami konversi ekspresi konstanta ke tipe input , tipe yang dipersempit adalah tipe konstanta. - Jika
P
adalah polaor
, jenis yang dipersempit adalah tipe umum dari jenis yang dipersempit dari pola sub jika jenis umum semacam itu ada. Untuk tujuan ini, algoritma jenis umum hanya mempertimbangkan konversi identitas, pembungkusan, dan referensi implisit, dan mempertimbangkan semua subpola dari rangkaian polaor
(mengabaikan pola yang ada dalam tanda kurung). - Jika
P
adalah polaand
, jenis sempit adalah jenis sempit dari pola yang benar. Selain itu, jenis dipersempit dari pola kiri adalah jenis input dari pola kanan. - Jika tidak, jenis input dari
P
adalah tipe yang dipersempit dariP
.
Hasil: Semantik penyempitan di atas telah diimplementasikan.
Definisi variabel dan penetapan yang pasti
Penambahan pola or
dan not
menciptakan beberapa masalah baru yang menarik terkait variabel pola dan penugasan yang pasti. Karena variabel biasanya dapat dideklarasikan paling banyak sekali, tampaknya variabel pola apa pun yang dideklarasikan di satu sisi pola or
mungkin tidak akan ditetapkan dengan jelas ketika pola tersebut cocok. Demikian pula, variabel yang dideklarasikan di dalam pola not
tidak akan diharapkan untuk secara pasti ditetapkan ketika pola cocok. Cara paling sederhana untuk mengatasinya adalah dengan melarang mendeklarasikan variabel pola dalam konteks ini. Namun, ini mungkin terlalu ketat. Ada pendekatan lain yang perlu dipertimbangkan.
Salah satu skenario yang perlu dipertimbangkan adalah ini
if (e is not int i) return;
M(i); // is i definitely assigned here?
Ini tidak berfungsi hari ini karena, untuk pola-ekspresi, variabel pola dianggap pasti ditetapkan hanya ketika pola-ekspresi benar ("pasti ditetapkan ketika benar").
Mendukung ini akan lebih sederhana (dari perspektif programmer) daripada juga menambahkan dukungan untuk pernyataan if
kondisi yang dinegasikan. Bahkan jika kita menambahkan dukungan seperti itu, programmer akan bertanya-tanya mengapa cuplikan di atas tidak berfungsi. Di sisi lain, skenario yang sama dalam switch
tidak masuk akal, karena tidak ada titik yang sesuai dalam program di mana pasti ditetapkan ketika false akan bermakna. Apakah kita akan mengizinkan ini dalam is-pattern-expression tetapi tidak dalam konteks lain di mana pola diizinkan? Tampaknya tidak teratur.
Terkait dengan ini adalah masalah penugasan pasti dalam pola disjungtif .
if (e is 0 or int i)
{
M(i); // is i definitely assigned here?
}
Kami hanya akan mengharapkan i
ditetapkan dengan pasti ketika input bukan nol. Tetapi karena kita tidak tahu apakah input adalah nol atau bukan di dalam blok, i
belum tentu ditetapkan. Namun, bagaimana jika kita mengizinkan i
dinyatakan dalam pola yang berbeda yang saling eksklusif?
if ((e1, e2) is (0, int i) or (int i, 0))
{
M(i);
}
Di sini, variabel i
pasti ditetapkan di dalam blok, dan mengambil nilainya dari elemen lain dari tuple ketika elemen nol ditemukan.
Juga telah disarankan untuk mengizinkan variabel ditentukan (berkali-kali) dalam setiap kasus pada blok kasus.
case (0, int x):
case (int x, 0):
Console.WriteLine(x);
Untuk membuat salah satu dari pekerjaan ini, kita harus dengan hati-hati menentukan di mana beberapa definisi tersebut diizinkan dan dalam kondisi apa variabel tersebut dianggap pasti ditetapkan.
Jika kita memilih untuk menunda pekerjaan seperti itu sampai nanti (yang saya sarankan), kita dapat mengatakan dalam C# 9
- di bawah
not
atauor
, variabel pola mungkin tidak dideklarasikan.
Kemudian, kita akan memiliki waktu untuk mengembangkan beberapa pengalaman yang akan memberikan wawasan tentang kemungkinan nilai santai itu nanti.
Hasil: Variabel pola tidak dapat dideklarasikan di bawah pola not
atau or
.
Diagnostik, subsumpsi, dan kelelahan
Bentuk pola baru ini memperkenalkan banyak peluang baru untuk kesalahan programmer yang dapat didiagnosis. Kita perlu memutuskan jenis kesalahan apa yang akan kita diagnosis, dan cara melakukannya. Berikut adalah beberapa contoh:
case >= 0 and <= 100D:
Kasus ini tidak akan pernah cocok (karena input tidak dapat berupa int
dan double
). Kami sudah memiliki kesalahan ketika kami mendeteksi kasus yang tidak pernah cocok, tetapi kata-katanya ("Kasus pengalihan telah ditangani oleh kasus sebelumnya" dan "Pola telah ditangani oleh lengan sebelumnya dari ekspresi pengalihan") mungkin menyesatkan dalam skenario baru. Kita mungkin harus memodifikasi kata-kata untuk hanya mengatakan bahwa pola tidak akan pernah cocok dengan input.
case 1 and 2:
Demikian pula, ini akan menjadi kesalahan karena nilai tidak boleh 1
dan 2
.
case 1 or 2 or 3 or 1:
Kasus ini dimungkinkan untuk dicocokkan, tetapi or 1
di akhir tidak menambahkan arti pada pola. Saya sarankan kita harus bertujuan untuk menghasilkan kesalahan setiap kali beberapa konjungsi atau disjungsi dari pola majemuk tidak menentukan variabel pola atau mempengaruhi kumpulan nilai yang dicocokkan.
case < 2: break;
case 0 or 1 or 2 or 3 or 4 or 5: break;
Di sini, 0 or 1 or
tidak menambahkan apa pun ke kasus kedua, karena nilai-nilai tersebut akan ditangani oleh kasus pertama. Ini juga pantas mendapatkan kesalahan.
byte b = ...;
int x = b switch { <100 => 0, 100 => 1, 101 => 2, >101 => 3 };
Ekspresi pengalihan seperti ini harus dianggap lengkap (menangani semua nilai input yang mungkin).
Dalam C# 8.0, ekspresi pengalihan dengan input jenis byte
yang berbeda tidak dianggap komprehensif dalam C# 8. Untuk menangani kelengkapan pola hubungan secara tepat, kita juga harus menangani kasus ini. Ini secara teknis akan menjadi perubahan yang melanggar, tetapi tidak ada pengguna yang mungkin memperhatikan.
C# feature specifications