Bagikan melalui


11 Pola dan pencocokan pola

11.1 Umum

Pola adalah bentuk sindikat yang dapat digunakan dengan is operator (§12.14.12), dalam switch_statement (§13.8.3), dan dalam switch_expression (§12.11) untuk mengekspresikan bentuk data yang data masuknya akan dibandingkan. Pola mungkin rekursif, sehingga bagian data dapat dicocokkan dengan sub-pola.

Pola diuji terhadap nilai dalam sejumlah konteks:

  • Dalam pernyataan switch, pola label kasus diuji terhadap ekspresi pernyataan switch.
  • Dalam operator is-pattern , pola di sisi kanan diuji terhadap ekspresi di sebelah kiri.
  • Dalam ekspresi pengalihan, polaswitch_expression_arm diuji terhadap ekspresi di sisi kiri ekspresi switch.
  • Dalam konteks berlapis, sub-pola diuji terhadap nilai yang diambil dari properti, bidang, atau diindeks dari nilai input lainnya, tergantung pada formulir pola.

Nilai di mana pola diuji disebut nilai input pola.

11.2 Formulir pola

11.2.1 Umum

Pola mungkin memiliki salah satu formulir berikut:

pattern
    : declaration_pattern
    | constant_pattern
    | var_pattern
    | positional_pattern
    | property_pattern
    | discard_pattern
    ;

Beberapa poladapat menghasilkan deklarasi variabel lokal.

Setiap formulir pola mendefinisikan kumpulan jenis untuk nilai input tempat pola dapat diterapkan. Pola Pberlaku untuk jenis T jika T berada di antara jenis yang nilainya mungkin cocok dengan pola. Ini adalah kesalahan waktu kompilasi jika pola P muncul dalam program untuk mencocokkan nilai input pola (§11.1) jenis T jika P tidak berlaku untuk T.

Contoh: Contoh berikut menghasilkan kesalahan waktu kompilasi karena jenis waktu kompilasi v adalah TextReader. Variabel jenis TextReader tidak pernah dapat memiliki nilai yang kompatibel dengan referensi dengan string:

TextReader v = Console.In; // compile-time type of 'v' is 'TextReader'
if (v is string) // compile-time error
{
    // code assuming v is a string
}

Namun, berikut ini tidak menghasilkan kesalahan waktu kompilasi karena jenis waktu kompilasi v adalah object. Variabel jenis object dapat memiliki nilai yang kompatibel dengan referensi dengan string:

object v = Console.In;
if (v is string s)
{
    // code assuming v is a string
}

contoh akhir

Setiap formulir pola mendefinisikan kumpulan nilai yang polanya cocok dengan nilai saat runtime.

Urutan evaluasi operasi dan efek samping selama pencocokan pola (panggilan ke Deconstruct, akses properti, dan pemanggilan metode dalam System.ITuple) tidak ditentukan.

11.2.2 Pola deklarasi

declaration_pattern digunakan untuk menguji bahwa nilai memiliki jenis tertentu dan, jika pengujian berhasil, untuk secara opsional memberikan nilai dalam variabel jenis tersebut.

declaration_pattern
    : type simple_designation
    ;
simple_designation
    : discard_designation
    | single_variable_designation
    ;
discard_designation
    : '_'
    ;
single_variable_designation
    : identifier
    ;

Simple_designation dengan token _ akan dianggap sebagai discard_designation daripada single_variable_designation.

Jenis runtime nilai diuji terhadap jenis dalam pola menggunakan aturan yang sama yang ditentukan dalam operator is-type (§12.14.12.1). Jika pengujian berhasil, pola cocok dengan nilai tersebut. Ini adalah kesalahan kompilasi-waktu jika jenis adalah jenis nilai nullable (§8.3.12) atau jenis referensi nullable (§8.9.3). Bentuk pola ini tidak pernah cocok dengan null nilai.

Catatan: Ekspresi e is T is-type dan pola e is T _ deklarasi setara jika T bukan jenis nullable. catatan akhir

Mengingat nilai input pola (§11,1) e, jika simple_designationdiscard_designation, itu menunjukkan pembuangan (§9.2.9.2), dan nilai e tidak terikat pada apa pun. (Meskipun variabel yang dideklarasikan dengan nama _ mungkin berada dalam cakupan pada saat itu, variabel bernama tersebut tidak terlihat dalam konteks ini.) Jika tidak, jika simple_designationsingle_variable_designation, variabel lokal (§9.2.9) dari jenis yang diberikan yang dinamai oleh pengidentifikasi yang diberikan diperkenalkan. Variabel lokal tersebut diberi nilai nilai input pola saat pola cocok dengan nilai.

Kombinasi tertentu dari jenis statis nilai input pola dan jenis yang diberikan dianggap tidak kompatibel dan mengakibatkan kesalahan waktu kompilasi. Nilai jenis E statis dikatakan kompatibel dengan pola T jika ada konversi identitas, konversi referensi implisit atau eksplisit, konversi tinju, atau konversi pembuka kotak dari E ke T, atau jika salah satu E atau T merupakan jenis terbuka (§8.4.3). Pola deklarasi penamaan jenis Tberlaku untuk setiap jenis E yang E kompatibel dengan pola .T

Catatan: Dukungan untuk jenis terbuka dapat paling berguna saat memeriksa jenis yang mungkin berupa jenis struct atau kelas, dan tinju harus dihindari. catatan akhir

Contoh: Pola deklarasi berguna untuk melakukan pengujian jenis run-time dari jenis 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 */ }

contoh akhir

Contoh: Pola deklarasi dapat digunakan untuk menguji nilai jenis null: nilai jenis Nullable<T> (atau kotak T) cocok dengan pola T2 id jenis jika nilainya non-null dan T2 adalah T, atau beberapa jenis dasar atau antarmuka T. Misalnya, dalam fragmen kode

int? x = 3;
if (x is int v) { /* code using v */ }

Kondisi if pernyataan adalah true pada runtime dan variabel v memegang nilai 3 jenis int di dalam blok. Setelah blok variabel v berada dalam cakupan, tetapi tidak pasti ditetapkan. contoh akhir

11.2.3 Pola konstanta

constant_pattern digunakan untuk menguji nilai nilai input pola (§11,1) terhadap nilai konstanta yang diberikan.

constant_pattern
    : constant_expression
    ;

Pola Pkonstanta berlaku untuk jenis T jika ada konversi implisit dari ekspresi P konstanta ke jenis T.

Untuk pola Pkonstanta , nilai yang dikonversi adalah

  • jika jenis nilai input pola adalah jenis integral atau jenis enum, nilai konstanta pola dikonversi ke jenis tersebut; Sebaliknya
  • jika jenis nilai input pola adalah versi nullable dari jenis integral atau jenis enum, nilai konstan pola dikonversi ke jenis yang mendasarnya; Sebaliknya
  • nilai nilai konstanta pola.

Mengingat nilai input pola e dan pola P konstanta dengan nilai yang dikonversi v,

  • jika e memiliki jenis integral atau jenis enum, atau bentuk nullable dari salah satunya, dan v memiliki jenis integral, pola Pcocok dengan nilai e jika hasil ekspresi e == v adalah true; sebaliknya
  • pola P cocok dengan nilai e jika mengembalikan object.Equals(e, v).true

Contoh: Pernyataan switch dalam metode berikut menggunakan lima pola konstanta dalam label kasusnya.

static decimal GetGroupTicketPrice(int visitorCount)
{
    switch (visitorCount) 
    {
        case 1: return 12.0m;
        case 2: return 20.0m;
        case 3: return 27.0m;
        case 4: return 32.0m;
        case 0: return 0.0m;
        default: throw new ArgumentException(...);
    }
}

contoh akhir

Pola 11.2.4 Var

Var_patterncocok dengan setiap nilai. Artinya, operasi pencocokan pola dengan var_pattern selalu berhasil.

Var_patternberlaku untuk setiap jenis.

var_pattern
    : 'var' designation
    ;
designation
    : simple_designation
    | tuple_designation
    ;
tuple_designation
    : '(' designations? ')'
    ;
designations
    : designation (',' designation)*
    ;

Mengingat nilai input pola (§11,1) e, jika penandaandiscard_designation, itu menunjukkan pembuangan (§9.2.9.2), dan nilai e tidak terikat ke apa pun. (Meskipun variabel yang dideklarasikan dengan nama tersebut mungkin berada dalam cakupan pada saat itu, variabel bernama tersebut tidak terlihat dalam konteks ini.) Jika tidak, jika penunjukansingle_variable_designation, pada runtime nilai e terikat ke variabel lokal yang baru diperkenalkan (§9,2,9) dari nama tersebut yang jenisnya adalah jenis statis e, dan nilai input pola ditetapkan ke variabel lokal tersebut.

Ini adalah kesalahan jika nama var akan mengikat ke jenis tempat var_pattern digunakan.

Jika penandaan adalah tuple_designation, polanya setara dengan positional_pattern (§11,2,5) dari penetapan formulir(var, ... ) di mana penetapanadalah yang ditemukan dalam tuple_designation. Misalnya, polanya var (x, (y, z)) setara dengan (var x, (var y, var z)).

11.2.5 Pola posisi

positional_pattern memeriksa bahwa nilai input bukan null, memanggil metode yang sesuai Deconstruct (§12,7), dan melakukan pencocokan pola lebih lanjut pada nilai yang dihasilkan. Ini juga mendukung sintaks pola seperti tuple (tanpa jenis yang disediakan) ketika jenis nilai input sama dengan jenis yang berisi Deconstruct, atau jika jenis nilai input adalah jenis tuple, atau jika jenis nilai input adalah atau jika jenis nilai input adalah object atau System.ITuple dan jenis runtime ekspresi mengimplementasikan System.ITuple.

positional_pattern
    : type? '(' subpatterns? ')' property_subpattern? simple_designation?
    ;
subpatterns
    : subpattern (',' subpattern)*
    ;
subpattern
    : pattern
    | identifier ':' pattern
    ;

Mengingat kecocokan nilai input dengansubpattern)jenis( pola, metode dipilih dengan mencari dalam jenis untuk deklarasi Deconstruct yang dapat diakses dan memilih satu di antaranya menggunakan aturan yang sama seperti untuk deklarasi dekonstruksi. Ini adalah kesalahan jika positional_pattern menghilangkan jenis, memiliki satu subpattern tanpa pengidentifikasi, tidak memiliki property_subpattern dan tidak memiliki simple_designation. Ini membedakan antara constant_pattern yang dikurung dan positional_pattern. Untuk mengekstrak nilai yang cocok dengan pola dalam daftar,

  • Jika jenis dihilangkan dan jenis ekspresi input adalah jenis tuple, maka jumlah subpattern harus sama dengan kardinalitas tuple. Setiap elemen tuple dicocokkan dengan subpattern yang sesuai, dan kecocokan berhasil jika semua ini berhasil. Jika ada subpattern yang memiliki pengidentifikasi, maka itu akan memberi nama elemen tuple pada posisi yang sesuai dalam jenis tuple.
  • Jika tidak, jika cocok Deconstruct ada sebagai anggota jenis, itu adalah kesalahan waktu kompilasi jika jenis nilai input tidak kompatibel dengan pola dengan jenis. Pada runtime, 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 segar yang dihasilkan kompilator untuk menerima parameter output. Setiap nilai yang diterima dicocokkan dengan subpattern yang sesuai, dan kecocokan berhasil jika semua ini berhasil. Jika ada subpattern yang memiliki pengidentifikasi, maka itu akan memberi nama parameter pada posisi yang sesuai dari Deconstruct.
  • Jika tidak, jika jenis dihilangkan, dan nilai input berjenis object atau beberapa jenis yang dapat dikonversi ke System.ITuple oleh konversi referensi implisitSystem.ITuple, dan tidak ada pengidentifikasi yang muncul di antara subpattern, maka kecocokan menggunakan .
  • Jika tidak, polanya adalah kesalahan waktu kompilasi.

Urutan di mana subpattern dicocokkan pada runtime tidak ditentukan, dan kecocokan yang gagal mungkin tidak mencoba mencocokkan semua subpattern.

Contoh: Di sini, kami mendekonstruksi hasil ekspresi dan mencocokkan nilai yang dihasilkan dengan pola berlapis yang sesuai:

static string Classify(Point point) => point switch
{
    (0, 0) => "Origin",
    (1, 0) => "positive X basis end",
    (0, 1) => "positive Y basis end",
    _ => "Just a point",
};

public readonly struct Point
{
    public int X { get; }
    public int Y { get; }
    public Point(int x, int y) => (X, Y) = (x, y);
    public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}

contoh akhir

Contoh: Nama elemen tuple dan parameter Dekonstruksi dapat digunakan dalam pola posisional, sebagai berikut:

var numbers = new List<int> { 10, 20, 30 };
if (SumAndCount(numbers) is (Sum: var sum, Count: var count))
{
    Console.WriteLine($"Sum of [{string.Join(" ", numbers)}] is {sum}");
}

static (double Sum, int Count) SumAndCount(IEnumerable<int> numbers)
{
    int sum = 0;
    int count = 0;
    foreach (int number in numbers)
    {
        sum += number;
        count++;
    }
    return (sum, count);
}

Output yang dihasilkan adalah

Sum of [10 20 30] is 60

contoh akhir

11.2.6 Pola properti

property_pattern memeriksa bahwa nilai input bukan 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 subpatterndari property_pattern tidak berisi pengidentifikasi.

Ini adalah kesalahan kompilasi-waktu jika jenis adalah jenis nilai nullable (§8.3.12) atau jenis referensi nullable (§8.9.3).

Catatan: Pola pemeriksaan null keluar dari pola properti sepele. Untuk memeriksa apakah string s non-null, seseorang dapat menulis salah satu formulir berikut:

#nullable enable
string s = "abc";
if (s is object o) ...  // o is of type object
if (s is string x1) ... // x1 is of type string
if (s is {} x2) ...     // x2 is of type string
if (s is {}) ...

catatan akhir Mengingat kecocokan ekspresi e dengan jenis{ polaproperty_pattern_list}, itu adalah kesalahan waktu kompilasi jika ekspresi e tidak kompatibel dengan jenis T yang ditunjuk berdasarkan jenis. Jika jenis tidak ada, jenis diasumsikan sebagai jenis statis e. Setiap pengidentifikasi yang muncul di sisi kiri property_pattern_list harus menunjuk properti atau bidang T yang dapat dibaca yang dapat diakses. Jika simple_designationproperty_pattern ada, itu mendeklarasikan variabel pola jenis T.

Pada runtime, ekspresi diuji terhadap T. Jika ini gagal, maka kecocokan pola properti gagal, dan hasilnya adalah false. Jika berhasil, maka setiap bidang atau properti property_subpattern dibaca, dan nilainya cocok dengan pola yang sesuai. Hasil dari seluruh kecocokan false hanya jika hasil dari salah satu dari ini adalah false. Urutan di mana subpattern dicocokkan tidak ditentukan, dan kecocokan yang gagal mungkin tidak menguji semua subpattern saat runtime. Jika kecocokan berhasil dan simple_designationproperty_pattern adalah single_variable_designation, variabel yang dideklarasikan diberi nilai yang cocok.

property_pattern dapat digunakan untuk pencocokan pola dengan jenis anonim.

Contoh:

var o = ...;
if (o is string { Length: 5 } s) ...

contoh akhir

Contoh: Pemeriksaan jenis run-time dan deklarasi variabel dapat ditambahkan ke pola properti, sebagai berikut:

Console.WriteLine(TakeFive("Hello, world!"));  // output: Hello
Console.WriteLine(TakeFive("Hi!"));            // output: Hi!
Console.WriteLine(TakeFive(new[] { '1', '2', '3', '4', '5', '6', '7' }));  // output: 12345
Console.WriteLine(TakeFive(new[] { 'a', 'b', 'c' }));  // output: abc

static string TakeFive(object input) => input switch
{
    string { Length: >= 5 } s => s.Substring(0, 5),
    string s => s,
    ICollection<char> { Count: >= 5 } symbols => new string(symbols.Take(5).ToArray()),
    ICollection<char> symbols => new string(symbols.ToArray()),
    null => throw new ArgumentNullException(nameof(input)),
    _ => throw new ArgumentException("Not supported input type."),
};

Output yang dihasilkan adalah

Hello
Hi!
12345
abc

contoh akhir

11.2.7 Buang pola

Setiap ekspresi cocok dengan pola buang, yang menghasilkan nilai ekspresi yang dibuang.

discard_pattern
    : '_'
    ;

Ini adalah kesalahan waktu kompilasi untuk menggunakan pola buang dalam relational_expressionpolarelational_expressionis bentuk atau sebagai pola switch_label.

Catatan: Dalam kasus tersebut, untuk mencocokkan ekspresi apa pun, gunakan var_pattern dengan buang var _. catatan akhir

Contoh:

Console.WriteLine(GetDiscountInPercent(DayOfWeek.Friday));
Console.WriteLine(GetDiscountInPercent(null));
Console.WriteLine(GetDiscountInPercent((DayOfWeek)10));

static decimal GetDiscountInPercent(DayOfWeek? dayOfWeek) => dayOfWeek switch
{
    DayOfWeek.Monday => 0.5m,
    DayOfWeek.Tuesday => 12.5m,
    DayOfWeek.Wednesday => 7.5m,
    DayOfWeek.Thursday => 12.5m,
    DayOfWeek.Friday => 5.0m,
    DayOfWeek.Saturday => 2.5m,
    DayOfWeek.Sunday => 2.0m,
    _ => 0.0m,
};

Output yang dihasilkan adalah

5.0
0.0
0.0

Di sini, pola buang digunakan untuk menangani null dan nilai bilangan bulat apa pun yang tidak memiliki anggota enumerasi yang DayOfWeek sesuai. Itu menjamin bahwa switch ekspresi menangani semua kemungkinan nilai input. contoh akhir

11.3 Subsumpsi pola

Dalam pernyataan pengalihan, itu adalah kesalahan jika pola kasus disubsumsikan oleh kumpulan kasus yang tidak dijaga sebelumnya (§13.8.3). Secara informal, ini berarti bahwa nilai input apa pun akan dicocokkan oleh salah satu kasus sebelumnya. Aturan berikut menentukan kapan sekumpulan pola mensubsumsi pola tertentu:

Pola Pakan cocok dengan konstanta K jika spesifikasi untuk perilaku runtime pola tersebut cocok dengan PK.

Sekumpulan pola mensubsumsi Qpola P jika salah satu kondisi berikut menahan:

  • Padalah pola konstanta dan salah satu pola dalam set Q akan cocok Pdengan nilai yang dikonversi
  • Padalah pola var dan kumpulan pola lengkap Q
  • Padalah pola deklarasi dengan jenis T dan kumpulan pola Q lengkap untuk jenis T (§11.4).

11.4 Kelelahan pola

Secara informal, satu set pola lengkap untuk jenis jika, untuk setiap nilai yang mungkin dari jenis itu selain null, beberapa pola dalam set berlaku. Aturan berikut menentukan kapan sekumpulan pola lengkap untuk jenis:

Sekumpulan pola Q lengkap untuk jenis T jika salah satu kondisi berikut menahan:

  1. T adalah jenis integral atau enum, atau versi nullable dari salah satunya, dan untuk setiap nilai yang mungkin dari Tjenis yang mendasar yang tidak dapat diubah ke null, beberapa pola akan Q cocok dengan nilai tersebut; atau
  2. Beberapa pola dalam Q adalah pola var; atau
  3. Beberapa pola dalam Q adalah pola deklarasi untuk jenis D, dan ada konversi identitas, konversi referensi implisit, atau konversi tinju dari T ke D.

Contoh:

static void M(byte b)
{
    switch (b) {
        case 0: case 1: case 2: ... // handle every specific value of byte
            break;
        // error: the pattern 'byte other' is subsumed by the (exhaustive)
        // previous cases
        case byte other: 
            break;
    }
}

contoh akhir