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.