C# Pattern Matching ve Record'lar

C# 8+ ile gelen pattern matching (switch expressions, property patterns) ve record türlerinin immutable yapısı sayesinde daha temiz, hatasız ve okunabilir kod yazma pratikleri gösterilir.

C# Pattern Matching ve Record'lar

C# Pattern Matching ve Record'lar: Immutability & Clean Code Yaklaşımı

C# 7 ile başlayıp 8, 9 ve 11 ile zirveye ulaşan pattern matching ile record types, fonksiyonel programlamanın gücünü nesne yönelimli dünyaya taşıdı. Bu araçlar, kodunuzdaki "if-else sarmallarını" ve "mutasyona açık veri taşıyıcılarını" tarihe gömer.

1. Pattern Matching: Sadece if değil, Akıl Okuyan Kod

Eskiden tip kontrolü ve değer karşılaştırması için onlarca satır is ve as kontrolleri yapardık. Artık desen eşleştirme (Pattern Matching) ile bu işlemleri tek bir ifadede çözebiliriz.

  • switch Expression (C# 8): Geleneksel switch-case'in aksine, bu bir ifade (expression) olduğu için değer döndürür ve break gerektirmez.

    csharp

    // Eski klasik switch (cümle)
    string GetDiscount(Season season) => season switch
    {
        Season.Spring => "%10",
        Season.Summer => "%20",
        Season.Autumn => "%5",
        Season.Winter => "%15",
        _ => "%0" // discard pattern (varsayılan)
    };
  • Property Pattern (C# 8): Nesnenin özelliklerine göre eşleştirme yapar. Özellikle polimorfizm yerine veri merkezli iş mantığı yazarken can kurtarır.

    csharp

    decimal CalculateTax(Order order) => order switch
    {
        { IsDomestic: true, Total: > 1000 } => order.Total * 0.08m, // Yurt içi ve 1000+ TL
        { IsDomestic: false, Total: > 500 } => order.Total * 0.18m, // Yurt dışı
        { Customer: { IsPremium: true } } => order.Total * 0.05m,  // Premium müşteri (iç içe property)
        _ => order.Total * 0.10m
    };
  • Positional Pattern (C# 9+): Deconstruct metodu olan tipler üzerinde çalışır. Tuple veya record'ları adeta bir puzzle gibi parçalar.

    csharp

    // Koordinatı ekrana basmak
    string Describe(Point p) => p switch
    {
        (0, 0) => "Merkez",
        (var x, 0) => $"X ekseninde {x}",
        (0, var y) => $"Y ekseninde {y}",
        var (x, y) => $"Özgür nokta: {x}, {y}"
    };

2. Record Types: Value Semantics ile Tanışın (C# 9)

Record, bir referans tipi (class) olmasına rağmen değer tipi (struct) gibi davranan, yani içeriğine göre eşitlik (value-based equality) sağlayan bir canavardır.

  • Immutability (Değişmezlik): Varsayılan olarak record'lar init özelliklerle gelir. Yani oluşturulduktan sonra değiştirilemezler. Bu, çoklu thread ortamlarında (concurrency) güvenlik demektir.

    csharp

    public record Product(string Name, decimal Price, string Sku);
    
    var product = new Product("Laptop", 15000, "XYZ123");
    // product.Price = 16000; // HATA! init-only olduğu için değişmez.
  • with Expression (Değiştirilmiş Kopya): Değişmezliği bozmadan, mevcut record'un üzerine bir veya iki özelliği değiştirilmiş yeni bir kopyasını oluşturmak için with kullanılır.

    csharp

    var discountedProduct = product with { Price = product.Price * 0.9m };
    // Eski product hala 15000, yeni product 13500.
  • Deconstruction (Parçalama): Record'lar otomatik olarak Deconstruct metodu içerir. Pattern matching ile mükemmel uyum sağlar.

    csharp

    var (name, price, sku) = product; // Tuple gibi parçala
    Console.WriteLine(name); // Laptop

3. Clean Code Pratikleri ve Sık Yapılan Hatalar

  • record class vs record struct: C# 10 ile birlikte record'lar struct olarak da tanımlanabilir. Çok küçük ve sık oluşturulan veri transfer nesneleri (DTO) için record struct kullanmak heap tahsisini (alloc) azaltır.

  • Polimorfizm Yerine Desen Kullanın: Eğer bir işlem nesnenin tipine göre değişiyorsa ve bu işlem sürekli eklenip çıkıyorsa (Open/Closed prensibine aykırı), klasik OOP kalıtımı yerine switch expression ile desen eşleştirme kullanmak daha esnektir.

  • ToString Avantajı: Record'lar, tüm property'leri otomatik olarak debug çıktısına ekler. Console.WriteLine(product); çıktısı: Product { Name = Laptop, Price = 15000, Sku = XYZ123 } şeklinde gelir. Loglama için biçilmiş kaftandır.

⚠️ Kritik Uyarı - Referans Tiplerin Eşitliği:
Record'lar içindeki class tipindeki property'ler (örn. List<string>) için eşitlik kontrolü referans eşitliği yapar, içerik eşitliği yapmaz. Eğer içindeki koleksiyonun da içeriğine göre eşit olmasını istiyorsanız, koleksiyonu ImmutableArray veya IEnumerable ile oluşturup IEquatable arayüzünü manuel implemente etmelisiniz.

Sonuç:

  • Eğer bir veri taşıyıcı (DTO, Response Model, Value Object) yazıyorsanız → Record kullanın.

  • Eğer 3 farklı duruma göre aksiyon alan bir metot yazıyorsanız → Switch Expression kullanın.
    Bu iki araç, C# kodunuzu Java'dan C++'dan ayıran, modern ve bakımı kolay bir seviyeye taşır. Kod artık ne yaptığını değil, ne anlama geldiğini okura söyler.

Tüm yazılar