F#, TypeScript, Rust ve Kotlin gibi dillerde yıllardır bulunan union (birleşim) türleri, C# geliştiricilerinin en çok istediği özelliklerden biriydi. "Bu değer ya bir Kedi, ya bir Köpek, ya da bir Kuş olabilir; başka bir şey olamaz" demenin dil düzeyinde temiz bir yolu yoktu. Geliştiriciler bunun için soyut sınıf hiyerarşileri veya OneOf gibi kütüphaneler kullanıyordu. C# 15 ile, yani .NET 11 ile birlikte bu özellik dile eklendi. Bu yazıda union types'ı örneklerle anlatıyoruz.
Temel sözdizimi
Microsoft'un .NET blogunda paylaşılan örneğe göre bir union şöyle tanımlanır:
public record class Cat(string Name);
public record class Dog(string Name);
public record class Bird(string Name);
public union Pet(Cat, Dog, Bird);
Pet türündeki bir değişken yalnızca Cat, Dog veya Bird değerlerinden birini taşıyabilir.
Desen eşleme ile kullanım
string name = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
};
Asıl güç burada: Derleyici union'ın tüm durumlarını bildiği için switch ifadesinde bir durumu unutursanız uyarı verebilir. Yarın Pet'e yeni bir tür eklendiğinde, onu ele almayan her switch ifadesi derleme sırasında görünür hale gelir. Pattern matching özelliklerini zaten kullanıyorsanız union'lar bunu bir adım ileri taşıyor.
Varsayılan değer ve null
Bir union değişkeninin varsayılan değeri (default) hiçbir durumu taşımayabilir. Bu durumda switch ifadesine bir null kolu eklemek gerekir:
Pet pet = default;
var description = pet switch
{
Dog d => d.Name,
Cat c => c.Name,
Bird b => b.Name,
null => "evcil hayvan yok",
};
Nullable reference types açık olan projelerde bu davranışa dikkat edin.
Gerçek hayattan kullanım: Sonuç tipi
Union'ların en yaygın kullanım alanlarından biri, bir işlemin başarılı veya başarısız sonucunu istisna (exception) fırlatmadan ifade etmektir:
public record class Basarili(Siparis Siparis);
public record class StokYok(string UrunKodu);
public record class OdemeReddedildi(string Neden);
public union SiparisSonucu(Basarili, StokYok, OdemeReddedildi);
IResult Yanit(SiparisSonucu sonuc) => sonuc switch
{
Basarili b => Results.Ok(b.Siparis),
StokYok s => Results.Conflict($"Stokta yok: {s.UrunKodu}"),
OdemeReddedildi o => Results.BadRequest(o.Neden),
};
Bu yaklaşım, beklenen iş hatalarını istisnalardan ayırır ve API cevaplarını daha öngörülebilir kılar. Hata yönetimi yaklaşımları için hata yönetimi ve Problem Details yazılarımıza bakabilirsiniz. Sözdizimi ve ayrıntılar sürümler arasında değişebileceğinden kendi projenizde resmî dokümantasyondaki güncel örnekleri esas alın.
Kapalı hiyerarşiler (closed hierarchies)
C# 15'in bir diğer yeniliği closed düzenleyicisi. Kapalı bir sınıftan yalnızca aynı derleme (assembly) içinde türetme yapılabilir. Böylece klasik sınıf hiyerarşileri de "tüm alt türleri biliniyor" garantisi kazanır ve derleyici eksiksiz desen eşleme denetimi yapabilir. Ortak davranışı olan ve kalıtım gerektiren modellerde kapalı hiyerarşi; yalnızca "şunlardan biri" demek istediğiniz durumlarda union daha uygun bir araçtır.
OneOf ve benzeri kütüphanelerden geçiş
- Yeni kodda dilin kendi union'ını tercih edin; ek bağımlılık ve özel API öğrenme ihtiyacı ortadan kalkar.
- Mevcut kodu bir anda dönüştürmek yerine sınır katmanlarda (servis dönüş tipleri gibi) aşamalı geçin.
- Performansı kritik "sıcak yollarda" kutulama (boxing) ve bellek ayırma davranışını ölçün.
JSON serileştirme konusunda dikkat
Topluluk geri bildirimlerinde, union değerlerinin System.Text.Json ile serileştirildiğinde tür ayırt edici bir alan içermediği için geri okunurken sorun yaşanabileceği dile getirildi. API sınırlarında union'ları doğrudan JSON'a çevirmek yerine, açık bir "tip" alanı içeren veri aktarım nesneleri (DTO) kullanmak şimdilik daha güvenli bir yaklaşım. Kullandığınız sürümdeki güncel davranışı mutlaka test edin.
Sık sorulan sorular
Union types için .NET 11 şart mı?
Evet, özellik C# 15 ile geliyor ve .NET 11 SDK'sı gerektiriyor. .NET 11 RC sürümüyle önizleme ayarı olmadan kullanılabiliyor.
Union ile soyut sınıf arasındaki fark ne?
Soyut sınıf açık bir hiyerarşidir; başka derlemeler yeni alt türler ekleyebilir. Union ise kapalı bir kümedir; tüm durumlar tanımda bellidir ve derleyici bunu denetleyebilir.
Entity Framework ile union kullanabilir miyim?
Veritabanı eşlemesi için doğrudan desteğin kapsamını EF Core sürüm notlarından kontrol edin. Genellikle union'lar iş mantığı ve servis katmanında kullanılır, veritabanında klasik tablolar korunur.
Sonuç
Union types, C#'ta hata yönetimi, durum modelleme ve API tasarımını daha güvenli ve okunabilir hale getiriyor. .NET 11'e geçişi planlarken union'ların kodunuzda nerelerde fayda sağlayacağını şimdiden belirleyin. .NET projelerinizde modernizasyon için destek alabilirsiniz.