Bagikan melalui


Ringkasan pencocokan pola

Pencocokan pola adalah teknik di mana Anda menguji ekspresi untuk menentukan apakah memiliki karakteristik tertentu. Pencocokan pola C# menyediakan sintaks yang lebih ringkas untuk menguji ekspresi dan mengambil tindakan saat ekspresi cocok. "ekspresi is" mendukung pencocokan pola untuk menguji ekspresi dan secara kondisional mendeklarasikan variabel baru ke hasil ekspresi tersebut. "ekspresi switch" memungkinkan Anda melakukan tindakan berdasarkan pola pencocokan pertama untuk ekspresi. Kedua ekspresi ini mendukung kosakata pola yang kaya.

Artikel ini memberikan gambaran umum tentang skenario di mana Anda dapat menggunakan pencocokan pola. Teknik-teknik ini dapat meningkatkan keterbacaan dan kebenaran kode Anda. Untuk diskusi lengkap tentang semua pola yang dapat Anda terapkan, lihat artikel tentang pola dalam referensi bahasa.

Pemeriksaan null

Salah satu skenario paling umum untuk pencocokan pola adalah memastikan nilainya bukan null. Anda dapat menguji dan mengonversi jenis nilai nullable ke jenis dasarnya saat menguji untuk null menggunakan contoh berikut:

int? maybe = 12;

if (maybe is int number)
{
    Console.WriteLine($"The nullable int 'maybe' has the value {number}");
}
else
{
    Console.WriteLine("The nullable int 'maybe' doesn't hold a value");
}

Kode sebelumnya adalah pola deklarasi untuk menguji jenis variabel, dan menetapkannya ke variabel baru. Aturan bahasa membuat teknik ini lebih aman daripada banyak teknik yang lain. Variabel number hanya dapat diakses dan ditetapkan dalam bagian sebenarnya dari klausul if. Jika Anda mencoba mengaksesnya di tempat lain, baik dalam klausul else, atau setelah blok if, kompiler mengeluarkan kesalahan. Kedua, karena Anda tidak menggunakan operator ==, pola ini berfungsi saat jenis membebani operator ==. Itu menjadikannya cara ideal untuk memeriksa nilai referensi null, menambahkan pola not:

string? message = ReadMessageOrDefault();

if (message is not null)
{
    Console.WriteLine(message);
}

Contoh sebelumnya menggunakan pola konstanta untuk membandingkan variabel dengan null. not adalah pola logis yang cocok ketika pola yang dinegasikan tidak cocok.

Uji jenis

Penggunaan umum lainnya untuk pencocokan pola adalah menguji variabel untuk melihat apakah cocok dengan jenis tertentu. Misalnya, kode berikut menguji apakah variabel non-null dan mengimplementasikan antarmuka System.Collections.Generic.IList<T>. Jika ya, ia menggunakan properti ICollection<T>.Count pada daftar tersebut untuk menemukan indeks tengah. Pola deklarasi tidak cocok dengan nilai null, terlepas dari jenis waktu kompilasi variabel. Kode di bawah ini melindungi dari null, selain menjaga terhadap jenis yang tidak mengimplementasikan IList.

public static T MidPoint<T>(IEnumerable<T> sequence)
{
    if (sequence is IList<T> list)
    {
        return list[list.Count / 2];
    }
    else if (sequence is null)
    {
        throw new ArgumentNullException(nameof(sequence), "Sequence can't be null.");
    }
    else
    {
        int halfLength = sequence.Count() / 2 - 1;
        if (halfLength < 0) halfLength = 0;
        return sequence.Skip(halfLength).First();
    }
}

Pengujian yang sama dapat diterapkan dalam ekspresi switch untuk menguji variabel terhadap beberapa jenis yang berbeda. Anda dapat menggunakan informasi itu untuk membuat algoritma yang lebih baik berdasarkan jenis run-time tertentu.

Membandingkan nilai diskret

Anda juga dapat menguji variabel untuk menemukan kecocokan pada nilai tertentu. Kode berikut menunjukkan satu contoh di mana Anda menguji nilai terhadap semua nilai yang mungkin dinyatakan dalam enumerasi:

public State PerformOperation(Operation command) =>
   command switch
   {
       Operation.SystemTest => RunDiagnostics(),
       Operation.Start => StartSystem(),
       Operation.Stop => StopSystem(),
       Operation.Reset => ResetToReady(),
       _ => throw new ArgumentException("Invalid enum value for command", nameof(command)),
   };

Contoh sebelumnya menunjukkan pengiriman metode berdasarkan nilai enumerasi. Kasus akhir _ adalah pola buang yang cocok dengan semua nilai. Ini menangani kondisi kesalahan di mana nilai tidak cocok dengan salah satu nilai enum yang ditentukan. Jika Anda menghilangkan lengan pengalih tersebut, pengkompilasi memperingatkan bahwa ekspresi pola Anda tidak menangani semua nilai input yang mungkin. Pada durasi, ekspresi switch melemparkan pengecualian jika objek yang diperiksa tidak cocok dengan salah satu lengan pengalih. Anda dapat menggunakan konstanta numerik alih-alih sekumpulan nilai enum. Anda juga dapat menggunakan teknik serupa untuk nilai string konstanta yang mewakili perintah:

public State PerformOperation(string command) =>
   command switch
   {
       "SystemTest" => RunDiagnostics(),
       "Start" => StartSystem(),
       "Stop" => StopSystem(),
       "Reset" => ResetToReady(),
       _ => throw new ArgumentException("Invalid string value for command", nameof(command)),
   };

Contoh sebelumnya menunjukkan algoritma yang sama, tetapi menggunakan nilai string, bukan enum. Anda akan menggunakan skenario ini jika aplikasi Anda merespons perintah teks, bukan format data biasa. Mulai dari C# 11, Anda juga dapat menggunakan Span<char> atau ReadOnlySpan<char> untuk menguji nilai string konstanta, seperti yang ditunjukkan dalam contoh berikut:

public State PerformOperation(ReadOnlySpan<char> command) =>
   command switch
   {
       "SystemTest" => RunDiagnostics(),
       "Start" => StartSystem(),
       "Stop" => StopSystem(),
       "Reset" => ResetToReady(),
       _ => throw new ArgumentException("Invalid string value for command", nameof(command)),
   };

Dalam semua contoh ini, pola buang memastikan bahwa Anda menangani setiap input. Kompiler membantu Anda dengan memastikan setiap nilai input yang mungkin ditangani.

Pola relasional

Anda dapat menggunakan pola relasional untuk menguji bagaimana nilai dibandingkan dengan konstanta. Misalnya, kode berikut mengembalikan keadaan air berdasarkan suhu dalam Fahrenheit:

string WaterState(int tempInFahrenheit) =>
    tempInFahrenheit switch
    {
        (> 32) and (< 212) => "liquid",
        < 32 => "solid",
        > 212 => "gas",
        32 => "solid/liquid transition",
        212 => "liquid / gas transition",
    };

Kode sebelumnya juga menunjukkan konjungsi andpola logis untuk memeriksa apakah kedua pola relasional cocok. Anda juga dapat menggunakan pola disjungtif or untuk memeriksa apakah salah satu pola cocok. Dua pola relasional dikelilingi oleh tanda kurung, yang dapat Anda gunakan di sekitar pola apa pun untuk kejelasan. Dua lengan pengalih terakhir menangani kasus untuk titik leleh dan titik didih. Tanpa kedua lengan itu, kompiler memperingatkan Anda bahwa logika Anda tidak mencakup setiap input yang mungkin.

Kode sebelumnya juga menunjukkan fitur penting lain yang disediakan kompiler untuk ekspresi pencocokan pola: Kompiler memperingatkan Anda jika Anda tidak menangani setiap nilai input. Pengkompilasi juga mengeluarkan peringatan jika pola untuk lengan sakelar ditutupi oleh pola sebelumnya. Itu memberi Anda kebebasan untuk faktor ulang dan menyusun ulang ekspresi switch. Cara lain untuk menulis ekspresi yang sama adalah:

string WaterState2(int tempInFahrenheit) =>
    tempInFahrenheit switch
    {
        < 32 => "solid",
        32 => "solid/liquid transition",
        < 212 => "liquid",
        212 => "liquid / gas transition",
        _ => "gas",
};

Pelajaran utama dalam sampel sebelumnya, dan pemfaktoran ulang atau penyusunan ulang lainnya, adalah bahwa kompilator memvalidasi bahwa kode Anda menangani semua input yang mungkin.

Beberapa input

Semua pola yang tercakup sejauh ini telah memeriksa satu input. Anda dapat menulis pola yang memeriksa beberapa properti objek. Pertimbangkan berikut rekaman Order:

public record Order(int Items, decimal Cost);

Tipe rekaman posisi sebelumnya mendeklarasikan dua anggota pada posisi eksplisit. Muncul pertama adalah Items, maka pesanan Cost. Untuk informasi selengkapnya, lihat Rekaman.

Kode berikut memeriksa jumlah item dan nilai pesanan untuk menghitung harga diskon:

public decimal CalculateDiscount(Order order) =>
    order switch
    {
        { Items: > 10, Cost: > 1000.00m } => 0.10m,
        { Items: > 5, Cost: > 500.00m } => 0.05m,
        { Cost: > 250.00m } => 0.02m,
        null => throw new ArgumentNullException(nameof(order), "Can't calculate discount on null order"),
        var someObject => 0m,
    };

Dua lengan pertama memeriksa dua properti Order. Yang ketiga hanya memeriksa biaya. Pemeriksaan berikutnya terhadap null, dan pencocokan final dengan nilai lainnya. Jika jenis Order menentukan metode Deconstruct yang sesuai, Anda dapat menghilangkan nama properti dari pola dan menggunakan dekonstruksi untuk memeriksa properti:

public decimal CalculateDiscount(Order order) =>
    order switch
    {
        ( > 10,  > 1000.00m) => 0.10m,
        ( > 5, > 50.00m) => 0.05m,
        { Cost: > 250.00m } => 0.02m,
        null => throw new ArgumentNullException(nameof(order), "Can't calculate discount on null order"),
        var someObject => 0m,
    };

Kode sebelumnya menunjukkan pola posisi di mana properti didekonstruksi untuk ekspresi.

Pola daftar

Anda dapat memeriksa elemen dalam daftar atau array menggunakan pola daftar. Pola daftar menyediakan sarana untuk menerapkan pola ke elemen urutan apa pun. Selain itu, Anda dapat menerapkan pola buang (_) untuk mencocokkan elemen apa pun, atau menerapkan pola potongan untuk mencocokkan nol atau lebih elemen.

Pola daftar adalah alat berharga ketika data tidak mengikuti struktur reguler. Anda dapat menggunakan pencocokan pola untuk menguji bentuk dan nilai data alih-alih mengubahnya menjadi sekumpulan objek.

Pertimbangkan kutipan berikut dari file teks yang berisi transaksi bank:

04-01-2020, DEPOSIT,    Initial deposit,            2250.00
04-15-2020, DEPOSIT,    Refund,                      125.65
04-18-2020, DEPOSIT,    Paycheck,                    825.65
04-22-2020, WITHDRAWAL, Debit,           Groceries,  255.73
05-01-2020, WITHDRAWAL, #1102,           Rent, apt, 2100.00
05-02-2020, INTEREST,                                  0.65
05-07-2020, WITHDRAWAL, Debit,           Movies,      12.57
04-15-2020, FEE,                                       5.55

Ini adalah format CSV, tetapi beberapa baris memiliki lebih banyak kolom daripada yang lain. Lebih buruk lagi untuk diproses, satu kolom dalam WITHDRAWAL jenis berisi teks yang dihasilkan pengguna dan dapat berisi koma dalam teks. Pola daftar yang mencakup pola buang, pola konstanta, dan pola var untuk mengambil data proses nilai dalam format ini:

decimal balance = 0m;
foreach (string[] transaction in ReadRecords())
{
    balance += transaction switch
    {
        [_, "DEPOSIT", _, var amount]     => decimal.Parse(amount),
        [_, "WITHDRAWAL", .., var amount] => -decimal.Parse(amount),
        [_, "INTEREST", var amount]       => decimal.Parse(amount),
        [_, "FEE", var fee]               => -decimal.Parse(fee),
        _                                 => throw new InvalidOperationException($"Record {string.Join(", ", transaction)} is not in the expected format!"),
    };
    Console.WriteLine($"Record: {string.Join(", ", transaction)}, New balance: {balance:C}");
}

Contoh sebelumnya mengambil array string, di mana setiap elemen adalah satu bidang dalam baris. Kunci switch ekspresi pada bidang kedua, yang menentukan jenis transaksi, dan jumlah kolom yang tersisa. Setiap baris memastikan data dalam format yang benar. Pola buang (_) melompati bidang pertama, dengan tanggal transaksi. Bidang kedua cocok dengan jenis transaksi. Pencocokan elemen tersisa melompati ke bidang dengan jumlah. Pencocokan akhir menggunakan pola var untuk menangkap representasi string dari jumlah tersebut. Ekspresi menghitung jumlah untuk menambah atau mengurangi dari saldo.

Pola daftar memungkinkan Anda mencocokkan bentuk urutan elemen data. Anda menggunakan pola buang dan ilis untuk mencocokkan lokasi elemen. Anda menggunakan pola lain untuk mencocokkan karakteristik tentang elemen individual.

Artikel ini menyediakan tur jenis kode yang dapat Anda tulis dengan pencocokan pola di C#. Artikel berikut menunjukkan lebih banyak contoh menggunakan pola dalam skenario, dan kosakata lengkap pola yang tersedia untuk digunakan.

Lihat juga