Desen eşleştirmeye genel bakış

Desen eşleştirme , bir ifadenin belirli özelliklere sahip olup olmadığını belirlemek için test ettiğiniz bir tekniktir. C# desen eşleştirmesi, ifadeleri test etme ve bir ifade eşleştiğinde eylem gerçekleştirme için daha kısa söz dizimi sağlar. "is ifade", bir ifadeyi test etmek ve bu ifadenin sonucuna koşullu olarak yeni bir değişken bildirmek için desen eşleştirmeyi destekler. "switch ifade", bir ifade için ilk eşleşen desene göre eylemler gerçekleştirmenizi sağlar. Bu iki ifade zengin desen kelime dağarcığını destekler.

Bu makalede, desen eşleştirmeyi kullanabileceğiniz senaryolara genel bir bakış sağlanır. Bu teknikler kodunuzun okunabilirliğini ve doğruluğunu geliştirebilir. Uygulayabileceğiniz tüm desenleri tam olarak tartışmak için dil başvurusundaki desenler hakkındaki makaleye bakın.

Null denetimler

Desen eşleştirme için en yaygın senaryolardan biri, değerlerin olmadığından nullemin olmaktır. Aşağıdaki örneği kullanarak test ederken null null atanabilir bir değer türünü test edebilir ve temel türüne dönüştürebilirsiniz:

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");
}

Yukarıdaki kod, değişkenin türünü test etmek ve yeni bir değişkene atamak için kullanılan bir bildirim desenidir. Dil kuralları bu tekniği diğerlerinden daha güvenli hale getirir. değişkenine number yalnızca erişilebilir ve yan tümcesinin gerçek bölümünde if atanır. Yan tümcesinde veya bloğundan elseif sonra başka bir yere erişmeye çalışırsanız, derleyici bir hata döndürür. İkinci olarak, işlecini kullanmadığınızdan == , bir tür işleci aşırı yüklediğinde == bu düzen çalışır. Bu, null başvuru değerlerini denetlemek için ideal bir yoldur ve deseni not ekler:

string? message = ReadMessageOrDefault();

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

Yukarıdaki örnekte değişkeni nullile karşılaştırmak için sabit bir desen kullanılmıştır. not, olumsuzlanan desen eşleşmediğinde eşleşen mantıksal bir desendir.

Tür testleri

Desen eşleştirme için bir diğer yaygın kullanım, bir değişkenin belirli bir türle eşleşip eşleşmediğini görmek için test etmektir. Örneğin, aşağıdaki kod bir değişkenin null olup olmadığını test eder ve arabirimini System.Collections.Generic.IList<T> uygular. Varsa, ortadaki dizini bulmak için bu listedeki özelliğini kullanır ICollection<T>.Count . Bildirim deseni, değişkenin derleme zamanı türünden bağımsız olarak bir null değerle eşleşmiyor. Aşağıdaki nullkod, uygulaması IListolmayan bir türe karşı korumaya ek olarak ile de korur.

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();
    }
}

Aynı testler, bir switch değişkeni birden çok farklı türe karşı test etmek için bir ifadeye uygulanabilir. Belirli bir çalışma zamanı türüne göre daha iyi algoritmalar oluşturmak için bu bilgileri kullanabilirsiniz.

Ayrık değerleri karşılaştırma

Belirli değerlerde eşleşme bulmak için bir değişkeni de test edebilirsiniz. Aşağıdaki kod, bir değeri bir sabit listesi içinde bildirilen tüm olası değerlerle test ettiğiniz bir örneği gösterir:

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)),
   };

Önceki örnekte, bir numaralandırmanın değerine göre bir yöntem gönderimi gösterilmektedir. Son _ durum, tüm değerlerle eşleşen bir atma düzenidir. Değerin tanımlı enum değerlerden biriyle eşleşmediği tüm hata koşullarını işler. Bu anahtar kolunu atlarsanız, derleyici desen ifadenizin tüm olası giriş değerlerini işlemediği konusunda uyarır. Çalışma zamanında, switch incelenen nesne anahtar kollarından herhangi biriyle eşleşmiyorsa ifade bir özel durum oluşturur. Sabit listesi değerleri kümesi yerine sayısal sabitler kullanabilirsiniz. Komutları temsil eden sabit dize değerleri için de bu benzer tekniği kullanabilirsiniz:

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)),
   };

Yukarıdaki örnek aynı algoritmayı gösterir, ancak sabit listesi yerine dize değerlerini kullanır. Uygulamanız normal bir veri biçimi yerine metin komutlarına yanıt verirse bu senaryosunu kullanırsınız. C# 11'den başlayarak, aşağıdaki örnekte gösterildiği gibi sabit dize değerlerini test etmek için veya Span<char>ReadOnlySpan<char>kullanabilirsiniz:

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)),
   };

Tüm bu örneklerde, atma deseni her girişi işlemenizi sağlar. Derleyici, olası her giriş değerinin işlendiğinden emin olarak size yardımcı olur.

İlişkisel desenler

Bir değerin sabitlerle karşılaştırmasını test etmek için ilişkisel desenler kullanabilirsiniz. Örneğin, aşağıdaki kod Fahrenheit'teki sıcaklığa göre su durumunu döndürür:

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

Yukarıdaki kod, her iki ilişkisel desenin de eşleştiklerini denetlemek için konjonktif andmantıksal deseni de gösterir. Ayrıca, her iki desenin or de eşleşerek eşleşmediğini denetlemek için ayrık bir desen de kullanabilirsiniz. İki ilişkisel desen parantez içindedir ve netlik için herhangi bir desenin etrafında kullanabilirsiniz. Son iki anahtar kolu, erime noktası ve kaynama noktası için kasaları işler. Bu iki kol olmadan, derleyici mantığınızın olası her girişi kapsamadığı konusunda sizi uyarır.

Yukarıdaki kod, derleyicinin desen eşleştirme ifadeleri için sağladığı başka bir önemli özelliği de gösterir: Her giriş değerini işlemezseniz derleyici sizi uyarır. Derleyici ayrıca, bir anahtar kolu deseni önceki bir desen kapsamındaysa bir uyarı da sağlar. Bu sayede anahtar ifadelerini yeniden düzenleme ve yeniden sıralama özgürlüğüne sahip olursunuz. Aynı ifadeyi yazmanın başka bir yolu da şu olabilir:

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

Yukarıdaki örnekteki temel ders ve diğer tüm yeniden düzenleme veya yeniden sıralama işlemleri, derleyicinin kodunuzun tüm olası girişleri işlediğini doğrulamasıdır.

Birden çok giriş

Şimdiye kadar ele alınan tüm desenler tek bir girişi kontrol ediyor. Bir nesnenin birden çok özelliğini inceleyen desenler yazabilirsiniz. Aşağıdaki Order kaydı göz önünde bulundurun:

public record Order(int Items, decimal Cost);

Yukarıdaki konumsal kayıt türü, açık konumlarda iki üyeyi bildirir. Önce öğesinin Items, ardından siparişin Costöğesinin gösterilmesidir. Daha fazla bilgi için bkz . Kayıtlar.

Aşağıdaki kod, indirimli fiyatı hesaplamak için madde sayısını ve siparişin değerini inceler:

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,
    };

İlk iki kol, öğesinin iki özelliğini inceler Order. Üçüncüsü ise yalnızca maliyeti inceler. Sonraki denetimler ve nullson, diğer tüm değerlerle eşleşir. Order Türü uygun Deconstruct bir yöntem tanımlıyorsa, desenden özellik adlarını atlayabilir ve özellikleri incelemek için yapısızlaştırmayı kullanabilirsiniz:

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,
    };

Yukarıdaki kod, ifade için özelliklerin kaldırıldığı konumsal deseni gösterir.

Liste desenleri

Liste deseni kullanarak bir listedeki veya dizideki öğeleri de kontrol edebilirsiniz. Liste düzeni , bir dizinin herhangi bir öğesine desen uygulamak için bir yol sağlar. Ayrıca, herhangi bir öğeyi eşleştirmek için atma desenini (_) uygulayabilir veya sıfır veya daha fazla öğeyi eşleştirmek için bir dilim deseni uygulayabilirsiniz.

Liste desenleri, veriler normal bir yapıyı izlemediğinde değerli bir araçtır. Verilerin şeklini ve değerlerini bir nesne kümesine dönüştürmek yerine test etmek için desen eşleştirmeyi kullanabilirsiniz.

Banka işlemlerini içeren bir metin dosyasından aşağıdaki alıntıyı göz önünde bulundurun:

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

Bu bir CSV biçimidir, ancak bazı satırlarda diğerlerinden daha fazla sütun vardır. daha da kötüsü, türdeki WITHDRAWAL bir sütun kullanıcı tarafından oluşturulan metin içerir ve metinde virgül içerebilir. Değer işleme verilerini bu biçimde yakalamak için atma desenini, sabit desenini ve var desenini içeren bir liste deseni:

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}");
}

Yukarıdaki örnek, her öğenin satırda bir alan olduğu bir dize dizisi alır. İkinci switch alandaki ifade anahtarları, işlem türünü ve kalan sütunların sayısını belirler. Her satır, verilerin doğru biçimde olmasını sağlar. Atma deseni (_), işlemin tarihiyle birlikte ilk alanı atlar. İkinci alan, hareket türüyle eşleşir. Kalan öğeyle eşleşir, tutarla alana atlar. Son eşleşme, miktarın dize gösterimini yakalamak için var desenini kullanır. İfade, bakiyenin ekleneceği veya çıkarleneceği miktarı hesaplar.

Liste desenleri , veri öğeleri dizisinin şekliyle eşleşmenizi sağlar. Öğelerin konumuyla eşleşmesi için atma ve dilim desenlerini kullanırsınız. Tek tek öğelerle ilgili özellikleri eşleştirmek için başka desenler kullanırsınız.

Bu makalede, C# dilinde desen eşleştirme ile yazabileceğiniz kod türlerine ilişkin bir tur sağlanmıştır. Aşağıdaki makalelerde senaryolardaki desenleri kullanma örnekleri ve kullanılabilecek desenlerin tam sözlüğü gösterilmektedir.

Ayrıca bkz.