Pattern matching, C# 7 ile başladı ve her sürümde güçlendi. C# 11 ile liste desenleri, C# 12 ile daha esnek alias kullanımı geldi. Bugün bu özelliklerin tamamını gerçek dünya senaryolarıyla inceleyelim.

Temel: is ile Type Pattern

// Eski yöntem
if (obj is Shape)
{
    var shape = (Shape)obj; // İki kez cast
    Console.WriteLine(shape.Area);
}

// Pattern matching ile
if (obj is Shape shape) // Tek satırda hem kontrol hem cast
    Console.WriteLine(shape.Area);

// Null kontrolü
if (name is not null)
    Console.WriteLine(name.Length);

// Tip + null kontrolü birlikte
if (response is HttpResponseMessage { IsSuccessStatusCode: true } msg)
    Console.WriteLine(msg.Content);

switch Expression: Değer Döndüren switch

// Eski: switch statement — verbose ve tekrarlı
string GetLabel(Status status)
{
    switch (status)
    {
        case Status.Pending:   return "Bekliyor";
        case Status.Active:    return "Aktif";
        case Status.Completed: return "Tamamlandı";
        case Status.Cancelled: return "İptal";
        default:               throw new ArgumentOutOfRangeException();
    }
}

// Yeni: switch expression — sade ve ifadesel
string GetLabel(Status status) => status switch
{
    Status.Pending   => "Bekliyor",
    Status.Active    => "Aktif",
    Status.Completed => "Tamamlandı",
    Status.Cancelled => "İptal",
    _                => throw new ArgumentOutOfRangeException(nameof(status))
};

// Gerçek senaryo: HTTP status code → mesaj
string Describe(int code) => code switch
{
    200 => "OK",
    201 => "Created",
    400 => "Bad Request",
    401 => "Unauthorized",
    403 => "Forbidden",
    404 => "Not Found",
    >= 500 => "Server Error",
    _   => "Unknown"
};

Property Pattern

Nesnenin özelliklerine göre eşleştirme yapılabilir. İç içe özellikler de desteklenir:

// Tekli property
decimal GetDiscount(Order order) => order switch
{
    { Status: OrderStatus.Cancelled }          => 0m,
    { Customer.IsPremium: true, Total: > 1000 } => 0.20m, // İç içe property
    { Customer.IsPremium: true }               => 0.10m,
    { Total: > 500 }                           => 0.05m,
    _                                          => 0m
};

// Tip + property kombinasyonu
string Classify(Shape shape) => shape switch
{
    Circle   { Radius: > 10 }   => "Büyük daire",
    Circle   { Radius: > 0  }   => "Küçük daire",
    Rectangle { Width: var w, Height: var h } when w == h => "Kare",
    Rectangle => "Dikdörtgen",
    Triangle  { IsEquilateral: true } => "Eşkenar üçgen",
    _         => "Bilinmeyen şekil"
};

Tuple Pattern: Çoklu Değer Eşleştirme

// Durum geçiş makinesi — tuple pattern ile
TrafficLight Transition(TrafficLight light, bool emergencyVehicle) =>
    (light, emergencyVehicle) switch
    {
        (_, true)                      => TrafficLight.Red,   // Acil durum: kırmızı
        (TrafficLight.Red, false)      => TrafficLight.Green,
        (TrafficLight.Green, false)    => TrafficLight.Yellow,
        (TrafficLight.Yellow, false)   => TrafficLight.Red,
        _                              => light
    };

// Koordinat düzlemi
string GetQuadrant(int x, int y) => (x, y) switch
{
    (0, 0)        => "Orijin",
    (> 0, > 0)    => "1. Çeyrek",
    (< 0, > 0)    => "2. Çeyrek",
    (< 0, < 0)    => "3. Çeyrek",
    (> 0, < 0)    => "4. Çeyrek",
    (0, _)        => "Y ekseni",
    (_, 0)        => "X ekseni",
    _             => "Bilinmiyor"
};

Positional Pattern: Deconstruct ile

public record Point(double X, double Y);

string ClassifyPoint(Point p) => p switch
{
    (0, 0)     => "Orijin",
    (0, _)     => "Y ekseni üzerinde",
    (_, 0)     => "X ekseni üzerinde",
    (> 0, > 0) => "Pozitif çeyrek",
    _          => "Diğer"
};

// Kendi Deconstruct metodunuzla
public class DateRange
{
    public DateTime Start { get; init; }
    public DateTime End   { get; init; }

    public void Deconstruct(out DateTime start, out DateTime end)
        => (start, end) = (Start, End);
}

string DescribeRange(DateRange r) => r switch
{
    var (s, e) when s == e            => "Tek gün",
    var (s, e) when (e - s).Days <= 7 => "Bu hafta",
    var (s, e) when (e - s).Days <= 31=> "Bu ay",
    _                                 => "Uzun dönem"
};

List Pattern (C# 11)

// Dizi ve liste içeriğine göre eşleştirme
string DescribeList(int[] numbers) => numbers switch
{
    []              => "Boş liste",
    [var single]    => $"Tek eleman: {single}",
    [var a, var b]  => $"İki eleman: {a} ve {b}",
    [1, 2, ..]      => "1 ve 2 ile başlıyor",
    [.., 99]        => "99 ile bitiyor",
    [_, _, ..]      => "En az iki eleman var",
    _               => "Diğer"
};

// CSV parse örneği
string ParseCsvLine(string[] fields) => fields switch
{
    [var id, var name, var email]          => $"Kullanıcı: {name} ({email})",
    [var id, var name, var email, var role]=> $"Admin: {name}",
    [var id, ..]                           => $"ID: {id}, diğer alanlar eksik",
    []                                     => "Boş satır",
    _                                      => "Tanınmayan format"
};

when Guard: Ek Koşul

// when ile ek koşul ekleme
decimal CalculateTax(Product p) => p switch
{
    { Category: "food" } when p.IsOrganic          => p.Price * 0.01m,
    { Category: "food" }                            => p.Price * 0.08m,
    { Category: "electronics" } when p.Price > 5000 => p.Price * 0.18m,
    { Category: "electronics" }                     => p.Price * 0.12m,
    _ when p.Price > 10000                           => p.Price * 0.20m,
    _                                                => p.Price * 0.18m
};

Gerçek Dünya: API Yanıt İşleme

// Farklı HTTP yanıt tiplerine göre davranış
async Task<T> ProcessResponseAsync<T>(HttpResponseMessage response)
{
    return response switch
    {
        { StatusCode: HttpStatusCode.OK } =>
            await response.Content.ReadFromJsonAsync<T>()
                ?? throw new InvalidOperationException("Boş yanıt"),

        { StatusCode: HttpStatusCode.NotFound } =>
            throw new NotFoundException(response.RequestMessage?.RequestUri?.ToString()),

        { StatusCode: HttpStatusCode.Unauthorized } =>
            throw new UnauthorizedException("Token geçersiz veya süresi dolmuş"),

        { StatusCode: HttpStatusCode.TooManyRequests } r =>
            throw new RateLimitException(
                r.Headers.RetryAfter?.Delta ?? TimeSpan.FromSeconds(60)),

        { IsSuccessStatusCode: false } r =>
            throw new ApiException(r.StatusCode,
                await r.Content.ReadAsStringAsync()),

        _ => throw new InvalidOperationException($"Beklenmedik durum: {response.StatusCode}")
    };
}

Pattern matching, C#'ı daha ifadesel kılıyor: koşulları okumak, durum geçişlerini modellemek ve tür hiyerarşilerini yönetmek çok daha kolay. Özellikle switch expression ve property pattern'ı benimsemek, kod kalitesini fark edilir biçimde artırıyor.