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.
-
switchExpression (C# 8): Gelenekselswitch-case'in aksine, bu bir ifade (expression) olduğu için değer döndürür vebreakgerektirmez.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+):
Deconstructmetodu 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'larinitö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. -
withExpression (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çinwithkullanı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
Deconstructmetodu 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 classvsrecord 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çinrecord structkullanmak 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.
-
ToStringAvantajı: 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.