C# uygulamalarında koşul kontrolleri arttıkça kodun okunabilirliği ve bakım maliyeti olumsuz etkilenebilir. Özellikle iç içe geçmiş if-else blokları, tür dönüşümleri ve karmaşık switch yapıları zamanla anlaşılması zor bir hale gelir. C# pattern matching, bu sorunları çözmek için dilin sunduğu güçlü özelliklerden biridir.
Pattern matching sayesinde nesnelerin türlerini, özelliklerini, değer aralıklarını ve koleksiyon yapılarını daha anlaşılır ifadelerle kontrol edebiliyoruz. Ben özellikle iş kurallarının yoğun olduğu .NET projelerinde bu yaklaşımı tercih ediyorum. Bu rehberde temel sözdiziminden gelişmiş desenlere kadar farklı teknikleri, çalışan C# örnekleriyle inceleyeceğim.
C# Pattern Matching Nedir ve Neden Kullanılır?
Pattern matching, bir değerin belirli bir desene uyup uymadığını kontrol etmemizi sağlayan C# dil özelliğidir. Geleneksel karşılaştırmalardan farklı olarak yalnızca değer eşitliğini değil, nesnenin çalışma zamanı türünü, özelliklerini ve yapısını da değerlendirebilir.
Örneğin bir API'den gelen yanıtın türünü kontrol etmek, siparişin durumuna göre işlem yapmak veya kullanıcı yetkilerini değerlendirmek için desen eşleştirme kullanabiliriz.
Başlangıç olarak geleneksel tür kontrolüne bakalım:
object value = "Merhaba";
if (value is string)
{
string text = (string)value;
Console.WriteLine(text.Length);
}
Bu kod çalışır ancak önce tür kontrolü yapar, ardından aynı değeri yeniden dönüştürür. Declaration pattern kullanarak iki işlemi birleştirebiliriz:
object value = "Merhaba";
if (value is string text)
{
Console.WriteLine(text.Length);
}
Burada is string text ifadesi, değerin string türüyle uyumlu olup olmadığını kontrol eder. Eşleşme gerçekleşirse text değişkeni güvenli biçimde kullanılabilir.
Bu yaklaşımın temel avantajları şunlardır:
- Okunabilirlik: Tür kontrolü ve değişken tanımlama tek ifadede gerçekleştirilir.
- Tür güvenliği: Gereksiz açık tür dönüşümleri azaltılır.
- Bakım kolaylığı: Koşulların amacı daha belirgin hale gelir.
- Daha az iç içe blok: Karmaşık karar mekanizmaları sadeleştirilebilir.
- Derleyici desteği: Bazı erişilemeyen koşullar ve eksik switch durumları geliştirme sırasında tespit edilebilir.
Ancak pattern matching kullanmanın her zaman daha hızlı çalışan kod anlamına gelmediğini özellikle belirtmek isterim. Asıl kazanım, doğru kullanıldığında kodun anlaşılabilirliğini artırmasıdır.
C# Pattern Matching Türleri ve Kullanım Alanları
C#, farklı veri yapılarına uygun çeşitli desen eşleştirme teknikleri sunuyor. Hangi deseni kullanacağımızı belirlerken kontrol edilen verinin türüne ve uygulamak istediğimiz iş kuralına bakmalıyız.
| Pattern türü | Kullanım amacı | Örnek |
|---|---|---|
| Declaration Pattern | Tür kontrolü ve değişken oluşturma | value is string text |
| Constant Pattern | Sabit değer kontrolü | value is null |
| Relational Pattern | Sayısal aralık karşılaştırması | age is >= 18 |
| Logical Pattern | Birden fazla deseni birleştirme | age is >= 18 and < 65 |
| Property Pattern | Nesne özelliklerini kontrol etme | user is { IsActive: true } |
| Positional Pattern | Konumsal bileşenleri eşleştirme | point is (0, 0) |
| List Pattern | Koleksiyon yapısını eşleştirme | numbers is [1, 2, ..] |
Relational ve Logical Pattern Kullanımı
Sayısal aralık kontrollerinde relational pattern oldukça kullanışlıdır. Özellikle fiyatlandırma, yaş kontrolü, stok durumu ve puanlama gibi senaryolarda birden fazla karşılaştırmayı sadeleştirir.
int age = 25;
if (age is >= 18 and < 65)
{
Console.WriteLine("Çalışma yaşı aralığında.");
}
Burada and operatörü iki koşulun birlikte sağlanmasını ister. Alternatif koşullar için or, bir deseni olumsuzlamak için not kullanabiliriz.
int day = 6;
bool isWeekend = day is 6 or 7;
Console.WriteLine(isWeekend); // True
Bu örnekte 6 ve 7 değerleri hafta sonunu temsil ediyor. Gerçek bir projede günleri enum ile tanımlamak daha açıklayıcı olabilir; örnek yalnızca or deseninin davranışını göstermek içindir.
Null Kontrollerinde Pattern Matching
Nullable reference types kullandığımız projelerde null kontrolü sürekli karşımıza çıkar. C# ile bunu açık ve kısa biçimde ifade edebiliriz.
string? name = null;
if (name is not null)
{
Console.WriteLine(name.Length);
}
Buradaki is not null, referansın null olmadığını kontrol eder. Ayrıca bir nesnenin hem varlığını hem de özelliklerini aynı anda değerlendirebiliriz.
public record Customer(
string Name,
bool IsActive
);
Customer? customer =
new("Ahmet", true);
if (customer is { IsActive: true })
{
Console.WriteLine("Aktif müşteri.");
}
Bu örnekte customer null ise desen eşleşmez. Nesne mevcutsa IsActive özelliğinin true olması gerekir. Böylece ayrı bir null kontrolü yazmadan gerekli koşulu ifade etmiş oluruz.
Switch Expression ile Karmaşık Koşulları Sadeleştirme
Pattern matching özelliklerinden en sık yararlandığım yapılardan biri switch expression. Geleneksel switch statement birden fazla komut çalıştırmak için uygundur; switch expression ise koşula göre tek bir değer üretmek istediğimizde daha okunabilir olabilir.
Örneğin bir e-ticaret uygulamasında sipariş durumunu kullanıcıya gösterilecek metne dönüştürmek istediğimizi düşünelim.
Geleneksel If-Else Kullanımı
string GetOrderStatus(int status)
{
if (status == 1)
return "Hazırlanıyor";
else if (status == 2)
return "Kargoya Verildi";
else if (status == 3)
return "Teslim Edildi";
return "Bilinmeyen Durum";
}
Bu örnek küçük olduğu için yönetilebilir görünüyor. Ancak durum sayısı arttığında veya her koşula ek kurallar eklendiğinde kontrol akışını takip etmek zorlaşır.
Aynı İşlemin Switch Expression ile Yazılması
string GetOrderStatus(int status) =>
status switch
{
1 => "Hazırlanıyor",
2 => "Kargoya Verildi",
3 => "Teslim Edildi",
_ => "Bilinmeyen Durum"
};
Bu yaklaşımda her satır belirli bir durumun karşılığını gösterir. Okuyucu hangi girdinin hangi sonucu üreteceğini hızlıca anlayabilir.
Alt çizgi karakteri olan _, önceki desenlerle eşleşmeyen bütün değerleri karşılayan discard pattern'dır. Switch expression içinde bütün olası durumları ele almak önemlidir; aksi halde derleyici uyarısı alınabilir ve eşleşmeyen bir değer çalışma zamanında SwitchExpressionException oluşturabilir.
When ile Ek Koşullar Tanımlama
Bazı iş kuralları yalnızca sabit değerlerle ifade edilemez. Bu durumda switch expression içerisinde when kullanarak ek bir Boolean koşul tanımlayabiliriz.
string GetDiscount(
decimal total,
bool isPremium) =>
total switch
{
>= 1000m when isPremium
=> "Yüzde 20 indirim",
>= 1000m
=> "Yüzde 10 indirim",
>= 500m
=> "Yüzde 5 indirim",
_ => "İndirim yok"
};
Burada switch kolları yukarıdan aşağıya değerlendirilir ve ilk uygun kolun sonucu kullanılır. Bu nedenle premium müşteriye özel koşulu genel indirim koşulundan önce yazdım.
İş kuralları büyüdükçe koşul sıralaması daha kritik hale gelir. İndirim hesaplamasını test ederken özellikle sınır değerleri ve birden fazla koşulun sağlandığı durumları kontrol etmenizi öneririm.
Property ve Positional Pattern ile Nesneleri Eşleştirme
Gerçek projelerde kararlarımız çoğunlukla tek bir değişkene değil, nesnelerin birden fazla özelliğine bağlıdır. Property pattern kullanarak nesneyi gereksiz değişkenlere ayırmadan doğrudan değerlendirebiliriz.
Property Pattern ile Sipariş Kontrolü
Bir siparişin yalnızca ödemesi tamamlanmış, onaylanmış ve tutarı pozitif olduğunda işleme alınmasını istediğimizi varsayalım.
public record Order(
decimal Total,
bool IsPaid,
bool IsApproved
);
static bool CanProcess(Order? order) =>
order is
{
Total: > 0,
IsPaid: true,
IsApproved: true
};
Bu yöntem üç iş kuralını doğrudan bir arada gösterir. Bir sonraki geliştirici metodu incelediğinde hangi özelliklerin gerekli olduğunu ayrı ayrı koşulları takip etmeden anlayabilir.
Ayrıca iç içe özellikleri de kontrol edebiliriz. Örneğin siparişe ait teslimat adresinin ülke bilgisi üzerinden doğrulama yapılabilir.
public record Address(string Country);
public record Shipment(Address? Address);
Shipment? shipment =
new(new Address("TR"));
bool isDomestic = shipment is
{
Address: { Country: "TR" }
};
İç içe property pattern, özellikle API yanıtlarını ve veri transfer nesnelerini değerlendirirken kullanışlıdır. Ancak çok derin nesne yapılarında tek bir ifadeye bütün kuralları sıkıştırmak yerine yardımcı metotlar oluşturmayı tercih ederim.
Positional Pattern ve Record Kullanımı
Positional pattern, deconstruction destekleyen türlerin bileşenlerini konumlarına göre eşleştirir. Özellikle positional record yapılarında anlaşılır bir kullanım sunar.
public record Point(int X, int Y);
static string GetPosition(Point point) =>
point switch
{
(0, 0) => "Başlangıç noktası",
(0, _) => "Y ekseni",
(_, 0) => "X ekseni",
_ => "Diğer konum"
};
Point record türünün derleyici tarafından oluşturulan Deconstruct metodu, positional pattern kullanımını mümkün kılar. Normal sınıflarda ise uygun bir Deconstruct metodu tanımlanmalıdır.
Record yapılarının ne zaman tercih edilmesi gerektiğini daha ayrıntılı öğrenmek isterseniz C# record ve class farkları üzerine hazırladığım rehberi inceleyebilirsiniz.
List Pattern ile Koleksiyon Kontrolleri
C# 11 ile gelen list pattern, koleksiyonların belirli konumlardaki elemanlarını ve uzunluk yapısını kontrol etmek için kullanılır. Burada önemli ayrıntı, her IEnumerable koleksiyonunun doğrudan list pattern desteklememesidir. Desteklenen türlerin uygun uzunluk ve indeksleme özelliklerine sahip olması gerekir.
Örneğin bir API'den gelen sayısal değerlerin ilk elemanını kontrol edelim:
int[] numbers = { 10, 20, 30 };
bool startsWithTen =
numbers is [10, ..];
Console.WriteLine(startsWithTen); // True
Buradaki iki nokta karakteriyle yazılan .., kalan elemanları karşılayan slice pattern'dır. Bu sayede dizinin yalnızca ilk elemanını kontrol edip diğer elemanları serbest bırakabiliriz.
İlk ve Son Elemanı Kontrol Etme
int[] numbers = { 1, 5, 8, 10 };
bool isValid =
numbers is [1, .., 10];
Console.WriteLine(isValid); // True
Bu desen, dizinin 1 ile başlayıp 10 ile bitmesini ve en az iki eleman içermesini gerektirir. Aradaki elemanların sayısı veya değerleri önemli değildir.
Ben list pattern kullanımını özellikle sabit yapılı komut dizilerinde, ayrıştırılmış metin parçalarında ve küçük veri doğrulama işlemlerinde faydalı buluyorum.
Yalnızca ilk elemanın kontrolü gerekiyorsa basit bir uzunluk ve indeks karşılaştırması da yeterli olabilir. Desen eşleştirme, algoritmanın amacını daha açık anlatıyorsa tercih edilmelidir.
ASP.NET Core Projelerinde Pattern Matching Kullanımı ve En İyi Pratikler
Pattern matching yalnızca dil özelliklerini göstermek için kullanılan bir teknik değil. ASP.NET Core uygulamalarında yanıt üretme, iş kuralı doğrulama ve servis sonuçlarını sınıflandırma gibi alanlarda da yararlı olabilir.
Servis Sonuçlarını HTTP Yanıtlarına Dönüştürme
Örneğin bir kullanıcı oluşturma servisinin farklı sonuç türleri döndürdüğünü düşünelim. Her sonucu ayrı bir record olarak modelleyebiliriz.
public abstract record CreateUserResult;
public record UserCreated(int Id)
: CreateUserResult;
public record UserAlreadyExists
: CreateUserResult;
public record ValidationFailed(string Message)
: CreateUserResult;
Şimdi bu sonuçları bir ASP.NET Core Minimal API endpoint'inde değerlendirelim:
app.MapPost("/users", async (
CreateUserRequest request,
IUserService service) =>
{
CreateUserResult result =
await service.CreateAsync(request);
return result switch
{
UserCreated created =>
Results.Created(
$"/users/{created.Id}",
new { created.Id }),
UserAlreadyExists =>
Results.Conflict(
"Kullanıcı zaten mevcut."),
ValidationFailed failed =>
Results.BadRequest(
failed.Message),
_ => Results.Problem(
"Beklenmeyen servis sonucu.")
};
});
Bu örnekte CreateUserRequest ve IUserService, uygulamanın kendi sözleşmeleridir. Endpoint yalnızca servis sonucunun HTTP yanıtına dönüştürülmesinden sorumludur; kullanıcı oluşturma kuralları servis katmanında kalır.
Bu ayrım, özellikle kurumsal web uygulama geliştirme projelerinde endpoint kodunun sade tutulmasına yardımcı olur. Ayrıca her sonuç türü için bağımsız birim testleri yazılabilir.
Pattern Matching Kullanırken Kaçınılması Gereken Hatalar
Her koşulu tek satıra indirmek iyi bir tasarım anlamına gelmez. Özellikle iç içe property pattern ve birden fazla logical pattern bir araya geldiğinde okunabilirlik yeniden azalabilir.
Ben kod incelemelerinde aşağıdaki kontrol listesini kullanmayı öneriyorum:
- Desenin amacını kontrol edin: Koşulun neyi doğruladığı ilk bakışta anlaşılmalı.
- Null durumlarını değerlendirin: Null girdilerin beklenen sonucu ürettiğinden emin olun.
- Switch sıralamasını inceleyin: Daha özel koşulları genel koşullardan önce konumlandırın.
- Varsayılan davranışı belirleyin: Bilinmeyen bir durumun hata mı yoksa varsayılan sonuç mu üretmesi gerektiğine karar verin.
- Sınır değerlerini test edin: Özellikle >, >= ve < operatörleriyle tanımlanan aralıkları doğrulayın.
- Uzun desenleri bölün: Karmaşık iş kurallarını anlamlı isimlere sahip yardımcı metotlara taşıyın.
- Performansı ölçün: Desen eşleştirmenin otomatik olarak daha hızlı olduğunu varsaymayın.
2026 itibarıyla modern C# projelerinde pattern matching birçok farklı biçimde kullanılabiliyor. Ancak list pattern gibi özelliklerin kullanılabilirliği projenin C# dil sürümüne bağlıdır. Framework sürümü, SDK ve LangVersion yapılandırmasını birlikte değerlendirmek gerekir.
Eski bir uygulamada refactoring yaparken önce mevcut testleri çalıştırmak, ardından davranışı değiştirmeden koşulları sadeleştirmek güvenli bir yaklaşımdır. Daha geniş kapsamlı mimari düzenlemelerde ise özel yazılım geliştirme süreçlerinde uygulanan kod inceleme ve test disiplinleri önem kazanır.
Sık Sorulan Sorular
C# pattern matching nedir?
C# pattern matching, bir değerin türünü, özelliklerini veya yapısını belirli desenlerle karşılaştırma yöntemidir. is, switch statement ve switch expression içerisinde kullanılabilir. Daha açıklayıcı koşul kontrolleri oluşturmamızı sağlar.
Pattern matching ile switch expression arasındaki fark nedir?
Pattern matching genel bir desen eşleştirme mekanizmasıdır. Switch expression ise bu desenleri kullanarak bir girdiden sonuç üreten C# ifadesidir. Dolayısıyla switch expression, pattern matching özelliklerinden yararlanabilen yapılardan biridir.
C# pattern matching performansı artırır mı?
Pattern matching her durumda performans artışı sağlamaz. Oluşturulan makine kodu, kullanılan desenlere ve derleyici optimizasyonlarına göre değişebilir. Performansın kritik olduğu bölümlerde BenchmarkDotNet gibi araçlarla ölçüm yapmak daha doğru bir yaklaşımdır.
List pattern hangi C# sürümünde kullanılabilir?
List pattern özelliği C# 11 ile kullanıma sunulmuştur. İlgili sözdizimini kullanabilmek için projenin uygun dil sürümüyle derlenmesi gerekir. Ayrıca eşleştirilecek türün list pattern için gerekli yapısal özellikleri desteklemesi önemlidir.
Pattern matching ile null kontrolü yapılabilir mi?
Evet, is null ve is not null ifadeleriyle doğrudan null kontrolü yapılabilir. Property pattern kullanılarak bir nesnenin null olmaması ve belirli özellikleri aynı ifadede doğrulanabilir. Bu yaklaşım, özellikle nullable referanslarla çalışırken faydalıdır.
Pattern matching her if-else bloğunun yerine kullanılmalı mı?
Hayır, basit koşullarda klasik if-else kullanımı daha anlaşılır olabilir. Pattern matching özellikle tür kontrolü, çok durumlu karar mekanizmaları ve nesne özelliklerinin eşleştirilmesinde avantaj sağlar. Tercihi belirleyen temel ölçüt, kodun okunabilirliği ve bakım kolaylığı olmalıdır.
Sonuç: C# Pattern Matching ile Daha Sürdürülebilir Kod Yazın
C# pattern matching, modern C# geliştirme yaklaşımında okunabilirliği artırmak için kullanabileceğimiz önemli araçlardan biri. Tür kontrollerinden switch expression yapılarına, property pattern kullanımından koleksiyon eşleştirmeye kadar farklı senaryolarda koşulları daha açık biçimde ifade etmemize yardımcı oluyor.
Benim önerim, mevcut projelerinizde önce tekrarlanan tür dönüşümlerini ve uzun if-else zincirlerini incelemeniz. Ardından davranışı koruyan küçük refactoring adımlarıyla uygun noktaları desen eşleştirmeye dönüştürmeniz. Değişiklik öncesinde ve sonrasında birim testlerini çalıştırarak sonucu doğrulayabilirsiniz.
Modern .NET, C# ve backend geliştirme üzerine hazırladığım diğer teknik rehberlere yazılım geliştirme blogumdan ulaşabilirsiniz. Kurumsal uygulamalarınızda mevcut kodun iyileştirilmesi veya yeni bir yazılım projesinin geliştirilmesi için CodeStup özel yazılım geliştirme hizmetlerini inceleyerek proje ihtiyaçlarınızı paylaşabilirsiniz.
Siz projelerinizde en çok hangi pattern türünü kullanıyorsunuz? Mevcut bir if-else yapısını switch expression veya property pattern kullanarak sadeleştirmeyi deneyin ve ortaya çıkan kodu okunabilirlik açısından karşılaştırın.