C#'ta pattern matching, bir değerin tipini, yapısını veya içerdiği verileri tek bir ifade içinde sorgulayıp buna göre karar vermeyi sağlayan modern bir sözdizimidir; switch expression ise bu pattern'leri kullanarak klasik switch-case yapısının çok daha kısa ve okunabilir bir versiyonunu sunar. Kısaca cevaplamak gerekirse: klasik if-else ve switch-case, tip kontrolü ve dallanma mantığını adım adım yazdırırken; is pattern, when koşulu, deconstruction pattern ve switch expression aynı mantığı tek bir ifadede toplayarak hem kod tekrarını azaltır hem de derleyicinin eksik durumları fark etmesini sağlar. Bu yazıda her iki yaklaşımı da aynı örnek üzerinden karşılaştırarak hangi durumda hangisinin daha uygun olduğunu göstereceğiz.
Klasik if-else ve switch-case yapısının sınırları
Bir uygulamada farklı tiplere veya durumlara göre karar veren kod parçaları büyüdükçe, geleneksel if-else zincirleri hızla karmaşıklaşır. Her yeni koşul, zincire bir else if daha eklemek anlamına gelir ve bu satırlar arttıkça kodun akışını takip etmek, hangi koşulun hangi sonucu doğurduğunu görmek giderek zorlaşır. Özellikle iç içe geçmiş tip kontrolleri veya birden fazla alanın birlikte değerlendirildiği senaryolarda, okuyucu mantığı kavramak için tüm zinciri baştan sona taramak zorunda kalır.
Klasik switch-case yapısı bu sorunu kısmen çözer ama kendi yükünü de beraberinde getirir. Her case bloğunun sonuna break ya da return eklenmesi gerekir; bu tekrar eden ifadeler kodun asıl amacını gölgeleyen görsel bir gürültüye dönüşür. Bir geliştirici yanlışlıkla break eklemeyi unutursa akış bir sonraki case'e sızabilir, bu da fark edilmesi zor hatalara yol açar.
Bakım açısından asıl risk ise zamanla ortaya çıkar. Bir tip hiyerarşisine yeni bir sınıf eklendiğinde, geliştiricinin bunu fark edip ilgili if-else zincirine veya switch-case yapısına yeni bir dal eklemesi gerekir; ancak bu adım genellikle otomatik olarak hatırlatılmaz. Sonuç olarak yeni tip sessizce mevcut mantığın dışında kalabilir ve hata çalışma zamanında, en beklenmedik anda ortaya çıkar. Bu tür yapısal zayıflıkları erken fark edebilmek için temel sözdizimi bilginizi C# bilgi düzeyi ölçüm testi ile kontrol etmek, hangi konularda daha fazla pratiğe ihtiyacınız olduğunu görmenize yardımcı olabilir.
is pattern ile tip kontrolü ve atama

is pattern, bir nesnenin belirli bir tipte olup olmadığını kontrol etmekle kalmaz, aynı anda o nesneyi ilgili tipten bir değişkene de atar. Klasik yaklaşımda önce is ile tip kontrolü yapılır, ardından ayrı bir satırda (object) dönüşümü ile değişken elde edilirdi; bu iki adım artık tek satırda birleşir.
object shape = new Circle(5);
// Klasik yöntem
if (shape is Circle)
{
Circle c = (Circle)shape;
Console.WriteLine(c.Radius);
}
// is pattern ile
if (shape is Circle circle)
{
Console.WriteLine(circle.Radius);
}
İkinci örnekte circle değişkeni yalnızca if bloğu içinde geçerlidir ve tip dönüşümü hatasına karşı derleyici zaten güvence sağlar. Bu, hem yazım hem okuma açısından klasik dönüşüm satırına göre daha sade bir sonuç verir; aynı mantık ileride göreceğimiz switch expression içindeki pattern'lerin de temelini oluşturur.
switch expression sözdizimi ve klasik switch-case'ten farkı
switch expression, klasik switch-case ifadesinin aksine bir değer döndüren bir ifadedir; yani sonucu doğrudan bir değişkene atayabilir veya bir metottan geriye döndürebilirsiniz. Sözdizimi de buna uygun şekilde sadeleşir: her dal case ve : yerine => operatörüyle yazılır, break ifadesine gerek kalmaz.
string ShapeName(object shape) => shape switch
{
Circle => "Daire",
Rectangle => "Dikdörtgen",
Triangle => "Üçgen",
_ => "Bilinmeyen şekil"
};
Burada dikkat edilmesi gereken nokta, son satırdaki _ dalıdır. Switch expression, gelen değerin hangi dala uyacağını garanti edemediği durumlarda çalışma zamanında hata fırlatabileceği için, geliştiricinin olası tüm durumları kapsayan bir varsayılan dal tanımlaması beklenir. Derleyici, tanımlanan dalların ilgili tipin tüm olasılıklarını kapsayıp kapsamadığını değerlendirir ve eksik bir durum sezdiğinde geliştiriciyi uyarı ile bilgilendirir; bu da klasik switch-case'te sessizce atlanabilecek bir durumu yazım aşamasında görünür hale getirir. Bu tip exhaustiveness (kapsayıcılık) kontrolü, özellikle büyüyen tip hiyerarşilerinde yeni bir sınıf eklendiğinde ilgili dalın unutulmasını önlemesi açısından pratik bir güvenlik katmanı sağlar.
Bu sözdiziminin mantığını uygulamalı örneklerle pekiştirmek isteyenler için canlı yazılım eğitimi programları, modern C# sözdizimini gerçek kod senaryoları üzerinden adım adım işleyen bir ortam sunar. Switch expression'ın kısalığı yalnızca satır sayısını azaltmakla kalmaz, aynı zamanda mantığın tamamının tek bir ifade içinde toplanmasını sağlayarak kodun bir bütün olarak okunmasını kolaylaştırır.
when koşuluyla pattern'e ek filtre ekleme
Bir pattern bazen tip veya şekil olarak doğru olsa da, iş kuralı açısından yetersiz kalabilir. İşte tam bu noktada when koşulu devreye girer: bir pattern'in eşleşmesini, ek bir mantıksal ifadeye bağlı hâle getirir. Böylece "bu nesne şu tipte mi?" sorusunun yanına "bu nesne şu tipteyse ve şu değeri de sağlıyorsa" sorusunu ekleyebilirsiniz. Bu, aynı tipe ait farklı senaryoları tek tek if bloklarıyla ayırt etmek yerine, tek bir switch yapısı içinde okunabilir biçimde toplamanızı sağlar.
when kullanmadan önce genellikle iç içe koşul blokları ile karşılaşırsınız; önce tip kontrolü yapılır, sonra o tipin içinde bir değer kontrolü daha eklenir. when bu iki adımı tek satıra indirir ve sıralamayı önemli kılar: bir pattern kolu sırayla değerlendirilir, ilk uyan kol çalışır. Aşağıdaki örnekte aynı Order tipi için farklı tutar aralıklarına göre dallanma gösterilmektedir:
public string GetIndirimMesaji(Order order) => order switch
{
Order o when o.Total >= 1000 => "Yüksek tutarlı siparişe özel indirim uygulandı.",
Order o when o.Total >= 500 => "Orta seviye indirim uygulandı.",
Order o when o.Total > 0 => "Standart sipariş, indirim yok.",
_ => "Geçersiz sipariş tutarı."
};
Burada dikkat edilmesi gereken nokta, her kolun aynı tipi (Order) hedeflemesine rağmen farklı koşullarla ayrıştırılmasıdır. Bu yapı, özellikle fiyatlandırma kuralları, yetkilendirme senaryoları veya durum bazlı iş akışlarında iç içe geçmiş if-else zincirlerinin yerini doğrudan alabilir. Kod, hem yukarıdan aşağıya okunduğunda mantığı takip etmeyi kolaylaştırır hem de yeni bir koşul eklemek istediğinizde mevcut yapıyı bozmadan tek bir satır eklemenize izin verir.
Deconstruction pattern: nesneyi bileşenlerine ayırma

Deconstruction pattern, bir nesneyi tek bir bütün olarak değil, içindeki alanlara ayrıştırarak eşleştirmenizi sağlayan bir tekniktir. Amaç, nesnenin tipini kontrol etmekle yetinmeyip aynı anda o nesnenin taşıdığı değerleri de değişkenlere aktarmaktır. Bu sayede pattern kolunun sağ tarafında, ayrı satırlarda özellik okumak yerine, doğrudan ayrıştırılmış değerlerle işlem yapabilirsiniz. Bir sınıfın deconstruction pattern ile kullanılabilmesi için genellikle bir Deconstruct metoduna sahip olması gerekir; record tipler bu metodu otomatik olarak sağladığından deconstruction pattern ile doğal bir uyum gösterir.
Aşağıdaki örnekte bir Point nesnesi, koordinatlarına göre ayrıştırılarak farklı konum senaryolarına göre değerlendirilmektedir:
public record Point(int X, int Y);
public string GetKonum(Point point) => point switch
{
(0, 0) => "Orijin noktası",
(var x, 0) => $"X ekseni üzerinde, X={x}",
(0, var y) => $"Y ekseni üzerinde, Y={y}",
(var x, var y) => $"Genel konum: ({x}, {y})"
};
Bu yapı, özellikle koordinat, tarih aralığı veya birden fazla alandan oluşan basit veri taşıyıcılarıyla çalışırken oldukça pratik bir okunabilirlik sağlar. Nesnenin alanlarını tek tek çağırıp ayrı ayrı karşılaştırmak yerine, deconstruction pattern doğrudan yapının şekline bakarak karar verir. Bu da kodun hem daha kısa hem de niyetin daha açık görünmesini sağlar; okuyan kişi, hangi kombinasyonun hangi sonuca yol açtığını satır satır takip edebilir.
Aynı senaryo yan yana: alan hesaplama örneği
Klasik ve modern yaklaşımlar arasındaki farkı en net gösteren senaryolardan biri, farklı şekil tiplerine göre alan hesaplamasıdır. Klasik yaklaşımda genellikle bir ShapeType numaralandırması üzerinden switch-case kurulur ve her kolda nesne ilgili tipe dönüştürülerek hesaplama yapılır. Bu yöntem çalışır, ancak her yeni şekil eklendiğinde hem numaralandırmayı hem de switch bloğunu güncellemek gerekir; ayrıca tip dönüşümü (cast) sırasında hata yapma riski her zaman vardır.
public enum ShapeType { Circle, Rectangle, Triangle }
public double CalculateArea(Shape shape)
{
switch (shape.Type)
{
case ShapeType.Circle:
var circle = (Circle)shape;
return Math.PI * circle.Radius * circle.Radius;
case ShapeType.Rectangle:
var rectangle = (Rectangle)shape;
return rectangle.Width * rectangle.Height;
case ShapeType.Triangle:
var triangle = (Triangle)shape;
return 0.5 * triangle.Base * triangle.Height;
default:
throw new ArgumentException("Bilinmeyen şekil türü.");
}
}
Aynı mantık, tip pattern ve deconstruction pattern birlikte kullanılarak switch expression ile şu şekilde ifade edilebilir:
public double CalculateArea(Shape shape) => shape switch
{
Circle c => Math.PI * c.Radius * c.Radius,
Rectangle (var width, var height) => width * height,
Triangle t => 0.5 * t.Base * t.Height,
_ => throw new ArgumentException("Bilinmeyen şekil türü.")
};
İkinci örnekte artık ayrı bir numaralandırmaya ihtiyaç yoktur; her kol doğrudan ilgili sınıfın tipine göre eşleşir ve gerekli alanları deconstruction pattern ile doğrudan değişkenlere aktarır. Sonuç olarak kod hem daha az satırdan oluşur hem de yeni bir şekil eklendiğinde yalnızca yeni bir kol eklemek yeterli olur. Bu tür karşılaştırmalı örnekleri kendi projelerinizde denemek ve farklı senaryolarla pekiştirmek isterseniz, C# ve nesne yönelimli programlama üzerine yazılmış diğer içerikler konuyu farklı açılardan ele alarak öğrenme sürecinizi destekleyebilir.
Ne zaman klasik, ne zaman modern yapı tercih edilmeli
Pattern matching ve switch expression, klasik if-else ile switch-case yapılarını ortadan kaldırmak için değil, belirli senaryolarda daha az kodla daha güvenli bir sonuç üretmek için vardır. Hangi yapının seçileceği; kontrol edilen tip sayısına, kodun gelecekte genişleyip genişlemeyeceğine ve ekip içindeki yazım standardına bağlıdır. İki ya da üç durumu kapsayan basit bir kontrolde klasik if-else hâlâ en okunabilir seçenektir; okuyan kişi akışı yukarıdan aşağı takip eder ve ekstra sözdizimi öğrenmesi gerekmez.
Kontrol edilen tip veya durum sayısı arttıkça, özellikle nesnenin farklı alt tiplerine göre bir değer üretilmesi gerektiğinde switch expression daha sürdürülebilir hâle gelir. Her dalın bir değer döndürmek zorunda olması, geliştiricinin bir durumu atlamasını derleyici seviyesinde engeller; bu da özellikle birden fazla kişinin aynı kod tabanında çalıştığı ekiplerde önemli bir güvenlik katmanıdır. Kodun gelecekte yeni tiplerle genişleyeceği biliniyorsa, deconstruction pattern ve when koşuluyla yazılmış bir switch expression yeni bir dalın eklenmesini de daha az hataya açık kılar.
Ekip standardı da göz ardı edilmemesi gereken bir kriterdir. Bir ekip zaten klasik switch-case ve if-else üzerine kurulu bir kod tabanı yürütüyorsa, aynı dosyada iki farklı üslubu karıştırmak yerine tutarlılığı korumak genellikle daha değerlidir. Yeni başlayan bir geliştirici için de önce klasik yapıları sağlam biçimde kavramak, ardından pattern matching'in getirdiği kısayolları anlamlandırmak daha sağlıklı bir öğrenme sırasıdır. Bu noktada hangi yapıyı ne zaman seçeceğinize dair pratik kazanmak istiyorsanız, ücretsiz kariyer testi ile mevcut seviyenizi ölçüp yazılım öğrenme yolculuğunuzda hangi konulara öncelik vermeniz gerektiğini görebilirsiniz.
| Kriter | Klasik if-else / switch-case | Modern pattern matching / switch expression |
|---|---|---|
| Okunabilirlik | Az sayıda durumda akışı takip etmek kolaydır | Çok sayıda tip/durumda niyeti tek bakışta özetler |
| Satır sayısı | Her dal için ayrı blok ve dönüş ifadesi gerekir | Aynı mantık genellikle daha az satırda ifade edilir |
| Güvenlik | Unutulan bir durum fark edilmeyebilir | Değer döndürme zorunluluğu eksik dalları belirginleştirir |
| Genişletilebilirlik | Yeni durum eklemek mevcut yapıyı bozabilir | Yeni pattern eklemek genellikle daha izole bir değişikliktir |
Sık Sorulan Sorular
Switch expression her zaman switch-case'in yerini alır mı?
Hayır. Switch expression bir değer üretmek istediğinizde ve dallar arasında ortak bir sonuç tipi olduğunda uygundur; yan etki üretmek (ekrana yazdırma, dosya işlemi gibi) ya da karmaşık, çok satırlı işlemler gerektiğinde klasik switch-case veya if-else daha uygun kalabilir.
is pattern ile as operatörü arasındaki fark nedir?
as operatörü dönüşüm başarısız olursa null döner ve sonrasında ayrıca null kontrolü yapmanız gerekir. is pattern ise hem tip kontrolünü hem de değişkene atamayı tek adımda yapar, dönüşüm başarısız olduğunda ilgili kod bloğuna hiç girilmez.
switch expression'da varsayılan dal (_) neden önemlidir?
Varsayılan dal, tanımlanan pattern'lerin hiçbirine uymayan durumlar için bir çıkış noktası sağlar. Bu dal eksik bırakılırsa ve hiçbir pattern eşleşmezse çalışma zamanında hata oluşur; bu yüzden özellikle dışarıdan gelen veriyle çalışan switch expression'larda varsayılan dal eklemek güvenli bir alışkanlıktır.
when koşulu birden fazla pattern'e birlikte eklenebilir mi?
Her pattern'in kendi when koşulu ayrı ayrı tanımlanır; aynı dal içinde birden fazla koşulu birleştirmek isterseniz mantıksal operatörlerle (&& , ||) tek bir when ifadesi yazabilirsiniz. Böylece hem tip kontrolü hem de ek iş kuralı aynı dalda okunabilir şekilde ifade edilir.
Deconstruction pattern hangi durumlarda tercih edilmeli?
Bir nesnenin birden fazla özelliğine aynı anda bakarak karar vermeniz gerektiğinde deconstruction pattern kodu sadeleştirir. Özellikle koordinat, boyut veya kayıt (record) gibi birden fazla alanı olan tiplerle çalışırken, her alanı ayrı ayrı okumak yerine tek bir pattern içinde bileşenlerine ayırmak daha net bir anlatım sağlar.
Pattern matching ve switch expression, C#'ın tip bazlı karar yapılarını daha okunabilir hâle getiren araçlardır; ancak bu araçları doğru yerde kullanmak, önce klasik if-else ve switch-case mantığını sağlam kavramaktan geçer. Bu temelleri pratik alıştırmalarla pekiştirmek isteyenler C# bilgi testi ile mevcut seviyelerini ölçebilir.