Menu

C# Pattern Matching: is, switch Desenleri, Property ve Liste Desenleri

C#'ta pattern matching nasıl çalışır: tip ve sabit desenleriyle is operatörü, case desenleri ve when ile switch ifadeleri, switch expression'lar, property, tuple ve konumsal desenler, ilişkisel ve mantıksal desenler (and, or, not), liste desenleri ve her birini hangi C# sürümünün eklediği.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Pattern matching bir değeri bir şekle karşı test eder: "bir Circle mı?", "null mı?", "Status'u "paid" ve Total'ı 100'ün üzerinde mi?". Test başarılı olduğunda desen, değerin parçalarını yeni değişkenlere de çıkarabilir. C# 7'de is operatörüyle küçük başladı ve o zamandan beri her sürümde büyüdü, bu yüzden bu sayfa her biçimin sürümünü belirtir.

is tip deseni (C# 7)

C# 7'den önce bir tipi kontrol edip kullanmak iki adım sürüyordu: as artı bir null kontrolü ya da is artı bir cast. Tip deseni ikisini aynı anda yapar ve size tipli bir değişken verir:

Çıktı:

card ending 4242: 49.90
transfer from PT50...: 1200
no payment
other payment: 5

p is CardPayment card yalnızca p null olmayan bir CardPayment (ya da ondan türeyen bir sınıf) olduğunda doğrudur ve o zaman card onu doğru statik tiple tutar. Null bir değer bir tip deseniyle asla eşleşmez; null ödemenin null kontrolüne düşmesinin nedeni budur.

Kapsam ve kesin atama

Desen değişkeni çevreleyen blokta vardır, ama derleyici onu yalnızca eşleşmenin başarılı olduğunun bilindiği yerde okumanıza izin verir. Bu, erken çıkıp değişkeni sonrasında kullandığınız "koruma cümlesi" şeklinin çalışmasını sağlar:

Çıktı:

19.99
42 is over 40
price must be text

boxed is int n && n > 40 diğer yaygın şekli gösterir: değişken &&'nin sağında kullanılabilir, çünkü o taraf yalnızca eşleşme başarılı olduğunda çalışır. ||'nin sağında atanmamıştır ve derleyici bunu söyler (CS0165).

Desenler unbox da yapar: boxed is int n box'lanmış bir int için başarılı olur ve size istisna fırlatabilecek bir cast olmadan değeri verir. Nullable değer tipleri için maybe is int n tam olarak maybe'nin bir değeri olduğunda başarılı olur.

Sabit ve null desenleri

Bir sabit de bir desendir. x is null en çok kullanılanıdır ve C# 7 ayrıca x is 0, status is "paid" ya da bir enum üyesine izin verir:

if (customer is null) return;          // null check that ignores any == overload
if (count is 0) Console.WriteLine("empty");
if (level is LogLevel.Error) Alert();

Birçok ekip is null'ı == null'a tercih eder, çünkü tipteki bir operatör aşırı yüklemesiyle yönlendirilemez. C# 9 olumsuz biçimi ekler: is not null.

switch ifadelerinde desenler (C# 7)

Aynı tip desenleri case'leri etiketleyebilir ve bir when cümlesi bir koşul ekler. Case'ler yukarıdan aşağıya denenir, bu yüzden onları özelden genele sıralayın:

static string Describe(object o)
{
    switch (o)
    {
        case null:                           return "nothing";
        case int n when n < 0:               return "negative number";
        case int n:                          return $"number {n}";
        case string s when s.Length == 0:    return "empty text";
        case string s:                       return $"text of {s.Length} chars";
        case IEnumerable<int> list:          return "a sequence of numbers";
        default:                             return o.GetType().Name;
    }
}

Describe(-4);                 // negative number
Describe("");                 // empty text
Describe(new List<int>());    // a sequence of numbers
Describe(2.5);                // Double

Derleyici sırayı kontrol eder: daha önceki bir case onu kapsadığı için asla eşleşemeyecek bir case CS8120 hatasıdır. default, nerede görünürse görünsün her zaman en son değerlendirilir. Yukarıdaki ödeme örneğinde olduğu gibi if/is zincirleriyle yazılan aynı mantık, 7'den itibaren her C# sürümünde çalışır.

Switch expression'lar (C# 8)

Bir switch expression, "birkaç durumdan tek bir değer hesapla"nın kompakt biçimidir. Önce değer, sonra switch, sonra virgülle ayrılmış pattern => result kolları gelir ve _ hepsini yakalayandır:

public abstract record Shape;
public record Circle(double Radius) : Shape;
public record Rectangle(double Width, double Height) : Shape;

static double Area(Shape shape) => shape switch
{
    Circle c => Math.PI * c.Radius * c.Radius,
    Rectangle { Width: var w, Height: var h } => w * h,
    _ => throw new ArgumentException("Unknown shape", nameof(shape)),
};

Area(new Rectangle(3, 4));   // 12

Çalışma zamanında hiçbir kol eşleşmezse SwitchExpressionException fırlatır ve derleyici bazı girdilerin ele alınmadığını görebildiğinde uyarır (CS8509). Switch expression sayfası sözdizimini ayrıca ele alır; bu sayfanın geri kalanı kollara koyabileceğiniz desenlerle ilgilidir.

Property desenleri (C# 8, C# 10'da genişletildi)

Bir property deseni bir nesnenin property'lerini süslü parantez içindeki iç içe desenlere karşı eşleştirir. Nesne de null olmamalıdır, bu yüzden tek başına { } "null değil" demektir:

public record Address(string City, string Country);
public record Order(decimal Total, string Status, Address ShipTo, int Items);

static string Shipping(Order order) => order switch
{
    { Status: "cancelled" }                   => "no shipment",
    { ShipTo.Country: "PT", Total: >= 50m }   => "free, domestic",     // C# 10 dotted form
    { ShipTo.Country: "PT" }                  => "4.90, domestic",
    { Total: > 200m }                         => "free, international",
    _                                         => "12.00, international",
};

Shipping(new Order(60m, "paid", new Address("Porto", "PT"), 2));    // free, domestic
Shipping(new Order(260m, "paid", new Address("Lyon", "FR"), 2));    // free, international

C# 8 ve 9'da iç içe biçim gereklidir: { ShipTo: { Country: "PT" } }. C# 10 yukarıda kullanılan noktalı kısaltmaya izin verir. Property desenleri is ile de çalışır; bu da çok koşullu kontrolleri bir tanım gibi okunur yapar: if (order is { Status: "paid", Items: > 0 }).

İlişkisel ve mantıksal desenler (C# 9)

C# 9 karşılaştırmaları (<, <=, >, >=) ve aralıkları desenlere çeviren and, or ve not birleştiricilerini ekledi:

static string Grade(int score) => score switch
{
    < 0 or > 100      => "invalid",
    >= 90             => "A",
    >= 75 and < 90    => "B",
    >= 50             => "C",
    _                 => "F",
};

Grade(82);    // B
Grade(101);   // invalid

if (input is not null and not "") { ... }
if (c is >= 'a' and <= 'z' or >= 'A' and <= 'Z') { ... }   // and binds tighter than or

C# 7'deki karşılığı bir if/else if zinciri ya da when cümleli bir switch'tir (case int s when s >= 90:).

Tuple ve konumsal desenler (C# 8)

Aynı anda birkaç değer üzerinde switch yapmak bir tuple üzerinde switch yaparak gerçekleştirilir:

static string Quadrant(int x, int y) => (x, y) switch
{
    (0, 0)       => "origin",
    (> 0, > 0)   => "I",
    (< 0, > 0)   => "II",
    (< 0, < 0)   => "III",
    (> 0, < 0)   => "IV",
    _            => "on an axis",
};

Quadrant(-2, 5);   // II

Bir konumsal desen, record'lar dahil Deconstruct metodu olan her tip için aynısını yapar: case Point(0, 0): ya da p is Rectangle(var w, var h) && w == h.

Liste desenleri (C# 11)

Liste desenleri dizileri ve listeleri elemanlarına göre eşleştirir. .. herhangi sayıda elemanla eşleşir (bir "dilim") ve her eleman konumu başka herhangi bir deseni tutabilir:

static string Route(string[] args) => args switch
{
    []                         => "help",
    ["add", var item]          => $"add {item}",
    ["remove", var item, ..]   => $"remove {item}",
    [var cmd, ..]              => $"unknown command {cmd}",
};

Route(new[] { "add", "milk" });             // add milk
Route(new[] { "remove", "milk", "now" });   // remove milk

int[] nums = { 1, 2, 3, 4 };
if (nums is [1, .., var last]) Console.WriteLine(last);   // 4

Hangi sürüm neyi ekledi

DesenÖrnekSürüm
Değişkenli tip desenio is Circle cC# 7.0
Sabit desenix is null, n is 0C# 7.0
var desenix is var vC# 7.0
case etiketlerinde desenler, when korumalarıcase int n when n < 0:C# 7.0
Switch expression, discard _x switch { ... }C# 8
Property deseni{ Status: "paid" }C# 8
Tuple deseni(x, y) switch { (0, 0) => ... }C# 8
Konumsal desenPoint(0, 0)C# 8
İlişkisel desen> 100, <= 0C# 9
Mantıksal desenlernot null, >= 1 and <= 5, 'a' or 'b'C# 9
Çıplak tip desenicase Circle:, Circle => ...C# 9
Genişletilmiş property deseni{ ShipTo.City: "Porto" }C# 10
Liste deseni ve dilim[first, .., last]C# 11

Yaygın hatalar

  • Case'leri genelden özele sıralamak. case CardPayment c:'den önce case Payment p: ikincisini ulaşılamaz yapar (CS8120). Önce özel olan.
  • Bir tip deseninin null ile eşleşmesini beklemek. null is string s false'tur. null'ı kendi case'iyle ya da is null ile ele alın.
  • Desen değişkenini eşleşmenin başarısız olmuş olabileceği yerde okumak. a is int n || ...'den sonra n atanmamıştır (CS0165).
  • Virtual metotlar yerine uzun tip switch'leri. Her yeni alt sınıf aynı switch'i beş yerde düzenlemek anlamına geliyorsa, davranış sınıf hiyerarşisine aittir. Desenler dışarıdan gelen veriler (JSON şekilleri, mesajlar, girdi tuple'ları) ve kontrol ettiğiniz kapalı tip kümeleri için parlar.
  • Bir switch expression'da hepsini yakalayan kolu unutmak. Eşleşmeyen bir girdi çalışma zamanında istisna fırlatır; CS8509 uyarısını okuyun.

Sıkça Sorulan Sorular

C#'ta pattern matching nedir?

Pattern matching bir değeri bir şekle karşı test eder ve eşleştiğinde aynı adımda parçalarını değişkenlere çıkarabilir. if (shape is Circle c) tipi test eder ve size tipli bir c verir; order is { Status: "paid", Total: > 100 } property'leri test eder. Desenler is ifadelerinde, switch ifadelerinde ve switch expression'larda görünür.

C#'ta is bir değişkenle nasıl kullanılır?

if (obj is Customer c) { ... }, obj'nin (null olmayan) bir Customer olduğunu kontrol eder ve onu if'in içinde Customer tipli c'ye atar. Eski var c = obj as Customer; if (c != null) ikilisinin yerini alır. Değişken yalnızca testin doğru olduğunun bilindiği yerde kesin olarak atanmıştır, bu yüzden if (!(obj is Customer c)) return; c'yi if'ten sonra kullanılabilir bırakır.

C#'ta is null ile == null arasındaki fark nedir?

x is null her zaman referansın kendisini kontrol eder. x == null, tip bir == operatörünü aşırı yüklüyorsa onu çağırır; bu genellikle aynı cevabı verir ama garanti edilmez. Tersi için x != null, !(x is null) ya da C# 9'dan itibaren x is not null yazın.

C#'ta when ile switch pattern matching nasıl çalışır?

Bir case etiketi bir desen artı bir when koruması tutabilir: case Order o when o.Total > 100:. Case yalnızca desen eşleşirse ve koşul doğruysa eşleşir ve case'ler yukarıdan aşağıya denenir, bu yüzden daha özel olanları önce koyun. Daha önceki bir case onu zaten kapsadığı için hiç ulaşılamayacak bir case olduğunda derleyici CS8120 bildirir.

Hangi C# sürümü hangi desenleri ekledi?

C# 7.0: is ve case içinde tip, sabit ve var desenleri, ayrıca when. C# 8: switch expression'lar, property, tuple ve konumsal desenler. C# 9: ilişkisel (> 5), mantıksal (and, or, not) ve çıplak tip desenleri. C# 10: genişletilmiş property desenleri ({ Address.City: "Porto" }). C# 11: liste desenleri ([1, .., var last]).

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA