Ulaşım
- Adres:Batıkent Mh. 8910 Sk. 6. Etap 1H No: 18 Yeni Toki Eyyübiye / Şanlıurfa (Yeni Alım Satım Karşısı)
- Telefon:0 (545) 528 88 93
- eMail: info@alestaweb.com
C# 15'te beklenen en heyecan verici özelliklerden biri olan Discriminated Union (Ayrıştırılmış Birleşim) nedir? F# ve Rust gibi dillerden tanıdığımız bu güçlü özellik, artık C#'a da geliyor! Alesta Web olarak bu rehberde discriminated union nedir, nasıl kullanılır, pattern matching ile nasıl birleştirilir ve bugün hangi kütüphanelerle benzer işlevselliği elde edebileceğinizi detaylıca anlatıyoruz. Type safety ve exhaustive pattern matching sayesinde daha güvenli kod yazmanın yollarını keşfedin!
Discriminated Union (Ayrıştırılmış Birleşim), bir değerin birden fazla olası tipten sadece birini alabileceğini tanımlayan bir veri yapısıdır. Yani bir değişken "A veya B veya C" olabilir ama aynı anda ikisi olamaz. Alesta Web ekibi olarak bu kavramı en basit şekilde açıklayalım.
Bir API yanıtı (API response) şunlardan biri olabilir: - Başarılı sonuç (Success) + veri - Hata (Error) + hata mesajı - Yükleniyor (Loading) durumu Bu üç durum AYNI ANDA olamaz! İşte discriminated union tam da bunu modellemek için kullanılır.
F#, Rust, TypeScript ve Haskell gibi dillerde bu özellik yıllardır mevcut. C# geliştiricileri ise uzun süredir bu özelliği talep ediyordu. TIOBE Language of the Year 2025 ödülünü alan C#, sonunda bu özelliği kazanıyor!
Discriminated union, "tagged union", "sum type" veya "variant" olarak da bilinir. Hepsi aynı konsepti ifade eder (they all describe the same concept).
C# geliştiricileri discriminated union özelliğini yıllardır bekliyordu. İşte zaman çizelgesi:
| Tarih / Date | Gelişme / Development |
|---|---|
| 2017 | İlk GitHub proposal açıldı |
| 2020 | C# 9 pattern matching güçlendirildi |
| 2023 | OneOf kütüphanesi popülerleşti |
| Ağustos 2025 | Mads Torgersen C# 15 planlarını açıkladı |
| Kasım 2025 | C# 14 GA - union keyword proposal |
| Kasım 2026 (Beklenen) | C# 15 ile native discriminated union |
Alesta Web olarak C# Language Design ekibini yakından takip ediyoruz. Mads Torgersen (C# Lead Designer), Ağustos 2025'te yaptığı açıklamada union özelliğinin C# 15 için öncelikli olduğunu belirtti.
Şu an (Şubat 2026) discriminated union henüz C#'ta native olarak mevcut DEĞİL. C# 15 ile Kasım 2026'da gelmesi bekleniyor (expected to arrive with C# 15 in November 2026). Bu makalede hem gelecek syntax'ı hem de bugün kullanabileceğiniz alternatif yöntemleri göstereceğiz.
C# 15'te beklenen discriminated union syntax'ı şöyle görünüyor:
// C# 15 Proposed Syntax (Önerilen Söz Dizimi)
public union Shape
{
case Circle(double Radius);
case Rectangle(double Width, double Height);
case Triangle(double Base, double Height);
}
// Kullanım / Usage
Shape myShape = new Shape.Circle(5.0);
// Pattern Matching ile / With Pattern Matching
double area = myShape switch
{
Shape.Circle(var r) => Math.PI * r * r,
Shape.Rectangle(var w, var h) => w * h,
Shape.Triangle(var b, var h) => 0.5 * b * h
};
// Exhaustive! Tüm case'leri handle etmezseniz compiler error!
// Discriminated Union ile API yanıtı modelleme
public union ApiResult<T>
{
case Success(T Data);
case Error(string Message, int Code);
case Loading();
}
// Kullanım
public async Task<ApiResult<User>> GetUserAsync(int id)
{
try
{
var user = await _db.Users.FindAsync(id);
return user != null
? new ApiResult<User>.Success(user)
: new ApiResult<User>.Error("User not found", 404);
}
catch (Exception ex)
{
return new ApiResult<User>.Error(ex.Message, 500);
}
}
// Tüketim / Consumption
var result = await GetUserAsync(123);
var message = result switch
{
ApiResult<User>.Success(var user) => $"Welcome, {user.Name}!",
ApiResult<User>.Error(var msg, var code) => $"Error {code}: {msg}",
ApiResult<User>.Loading() => "Please wait..."
};
Discriminated union'ın en büyük avantajı exhaustive pattern matching (kapsamlı desen eşleştirme). Compiler, tüm case'lerin ele alındığını kontrol eder. Bir case'i unutursanız derleme hatası alırsınız (you'll get compile-time error)!
C#'ın güçlü pattern matching özellikleri discriminated union ile mükemmel uyum sağlar. Alesta Web olarak en yaygın kullanım senaryolarını gösterelim:
// Ödeme yöntemi union'ı
public union PaymentMethod
{
case CreditCard(string Number, string Expiry, string CVV);
case BankTransfer(string IBAN, string BIC);
case Crypto(string WalletAddress, string Network);
case Cash();
}
// İşlem / Processing
public string ProcessPayment(PaymentMethod method, decimal amount)
{
return method switch
{
PaymentMethod.CreditCard(var num, _, _) =>
$"Charging {amount:C} to card ending in {num[^4..]}",
PaymentMethod.BankTransfer(var iban, _) =>
$"Transferring {amount:C} to {iban}",
PaymentMethod.Crypto(var wallet, var network) =>
$"Sending {amount} to {wallet} on {network}",
PaymentMethod.Cash() =>
$"Cash payment of {amount:C} received"
};
}
// Validation ile pattern matching
string ValidatePayment(PaymentMethod method) => method switch
{
PaymentMethod.CreditCard(var num, _, _) when num.Length != 16 =>
"Invalid card number",
PaymentMethod.CreditCard(_, var exp, _) when IsExpired(exp) =>
"Card expired",
PaymentMethod.BankTransfer(var iban, _) when !IsValidIBAN(iban) =>
"Invalid IBAN",
PaymentMethod.Crypto(var wallet, _) when wallet.Length < 20 =>
"Invalid wallet address",
_ => "Valid payment method"
};
Pattern matching'de _ (discard) kullanarak kullanmadığınız değerleri atlayabilirsiniz. Bu kod okunurluğunu artırır (improves code readability).
C# 15 çıkana kadar bekleyemiyorsanız, discriminated union benzeri yapılar oluşturmanın birkaç yolu var. Alesta Web ekibi olarak en yaygın yöntemleri gösterelim:
// Abstract record base class
public abstract record Shape
{
// Private constructor - dışarıdan türetilemez
private Shape() { }
// Nested sealed records
public sealed record Circle(double Radius) : Shape;
public sealed record Rectangle(double Width, double Height) : Shape;
public sealed record Triangle(double Base, double Height) : Shape;
}
// Kullanım
Shape shape = new Shape.Circle(5.0);
double area = shape switch
{
Shape.Circle(var r) => Math.PI * r * r,
Shape.Rectangle(var w, var h) => w * h,
Shape.Triangle(var b, var h) => 0.5 * b * h,
_ => throw new InvalidOperationException() // Maalesef gerekli
};
Bu yöntemde exhaustive checking yok! Compiler yeni bir case eklediğinizde sizi uyarmaz. _ => throw satırını eklemek zorundasınız. Native discriminated union bu sorunu çözecek.
// Marker interface
public interface IApiResult<T> { }
// Implementations
public record Success<T>(T Data) : IApiResult<T>;
public record Error<T>(string Message, int Code) : IApiResult<T>;
public record Loading<T>() : IApiResult<T>;
// Kullanım
IApiResult<User> result = new Success<User>(user);
var output = result switch
{
Success<User> s => $"Got user: {s.Data.Name}",
Error<User> e => $"Error: {e.Message}",
Loading<User> => "Loading...",
_ => "Unknown state"
};
C#'ta discriminated union benzeri işlevsellik sağlayan birkaç popüler kütüphane var. Alesta Web olarak en çok kullanılanları inceledik:
// NuGet'ten kurulum
dotnet add package OneOf
// Kullanım
using OneOf;
// Generic union tanımlama
public OneOf<Success, NotFound, Error> GetUser(int id)
{
if (id <= 0) return new Error("Invalid ID");
var user = _db.Find(id);
if (user == null) return new NotFound();
return new Success(user);
}
// Tüketim - Match metodu ile (EXHAUSTIVE!)
var result = GetUser(123);
string message = result.Match(
success => $"Found: {success.User.Name}",
notFound => "User not found",
error => $"Error: {error.Message}"
);
// Bir case'i unutursanız DERLEME HATASI!
Match() metodu tüm case'leri handle etmenizi zorlar. Bir case'i unutursanız kod derlenmez! Bu, native discriminated union'ın sağladığı exhaustive checking'e en yakın çözüm.
// NuGet'ten kurulum
dotnet add package Thinktecture.Runtime.Extensions
// Discriminated union tanımlama
[Union]
public partial record Result
{
public record Success(string Value);
public record Error(string Message);
}
// Kullanım
Result result = Result.Success("Hello");
// Switch ve Map metodları ile
result.Switch(
success => Console.WriteLine(success.Value),
error => Console.WriteLine($"Error: {error.Message}")
);
// Map ile dönüşüm
string output = result.Map(
success => success.Value.ToUpper(),
error => $"FAILED: {error.Message}"
);
// NuGet'ten kurulum
dotnet add package LanguageExt.Core
using LanguageExt;
using static LanguageExt.Prelude;
// Either kullanımı (Left = Error, Right = Success)
Either<string, User> GetUser(int id) =>
id <= 0
? Left("Invalid ID")
: Right(_db.Find(id));
// Pattern matching
var result = GetUser(123);
result.Match(
Left: error => Console.WriteLine($"Error: {error}"),
Right: user => Console.WriteLine($"User: {user.Name}")
);
| Kütüphane | Avantajlar | Dezavantajlar |
|---|---|---|
| OneOf | Basit, hafif, exhaustive match | Generic syntax verbose |
| Thinktecture | Source generator, temiz syntax | Daha büyük dependency |
| LanguageExt | Tam FP ekosistemi | Öğrenme eğrisi yüksek |
Discriminated union neden bu kadar önemli? Alesta Web olarak en önemli avantajları listeleyelim:
Her case kendi tipine sahip. Yanlış tipe erişmeye çalıştığınızda compiler sizi uyarır.
Tüm olası durumları handle etmenizi zorunlu kılar. Bir case'i unutursanız derleme hatası alırsınız.
null yerine explicit "NotFound" veya "Empty" case'i kullanabilirsiniz. NullReferenceException riskini azaltır.
Kod kendini açıklar. Bir fonksiyonun döndürebileceği tüm durumlar union tanımında görünür.
// 1. API Response Handling
union ApiResponse<T> { Success(T), Error(string), Loading() }
// 2. Form Validation
union ValidationResult { Valid(), Invalid(List<string> Errors) }
// 3. State Machine
union OrderState { Pending(), Processing(), Shipped(string TrackingNo), Delivered(), Cancelled(string Reason) }
// 4. Option/Maybe Type (null alternative)
union Option<T> { Some(T Value), None() }
// 5. Result Type (exception alternative)
union Result<T, E> { Ok(T Value), Err(E Error) }
// 6. Command Pattern
union UserCommand { Create(UserDto), Update(int Id, UserDto), Delete(int Id) }
// 7. Event Sourcing
union DomainEvent { OrderCreated(Order), ItemAdded(Item), PaymentReceived(Payment) }
Discriminated union özellikle domain modeling ve error handling için mükemmeldir. Exception fırlatmak yerine Result tipi döndürmek, kodunuzu daha öngörülebilir yapar (makes your code more predictable).
Alesta Web olarak gerçek dünya senaryolarından örnekler paylaşalım:
// OneOf kütüphanesi ile
using OneOf;
using OneOf.Types;
public class PaymentService
{
public OneOf<PaymentSuccess, InsufficientFunds, CardDeclined, NetworkError>
ProcessPayment(PaymentRequest request)
{
// Bakiye kontrolü
if (request.Amount > GetBalance(request.CardNumber))
return new InsufficientFunds(GetBalance(request.CardNumber));
// Kart doğrulama
if (!ValidateCard(request.CardNumber))
return new CardDeclined("Card validation failed");
try
{
var transactionId = ChargeCard(request);
return new PaymentSuccess(transactionId, DateTime.UtcNow);
}
catch (HttpRequestException)
{
return new NetworkError("Payment gateway unreachable");
}
}
}
// Kullanım
var result = paymentService.ProcessPayment(request);
result.Switch(
success => SendConfirmationEmail(success.TransactionId),
insufficient => ShowError($"Yetersiz bakiye. Mevcut: {insufficient.Balance:C}"),
declined => ShowError($"Kart reddedildi: {declined.Reason}"),
network => RetryPayment(request)
);
// HTTP Response union
public abstract record HttpResult<T>
{
private HttpResult() { }
public sealed record Ok(T Data) : HttpResult<T>;
public sealed record Created(T Data, string Location) : HttpResult<T>;
public sealed record NoContent() : HttpResult<T>;
public sealed record BadRequest(Dictionary<string, string[]> Errors) : HttpResult<T>;
public sealed record NotFound(string Message) : HttpResult<T>;
public sealed record Unauthorized() : HttpResult<T>;
public sealed record InternalError(string Message) : HttpResult<T>;
}
// Controller'da kullanım
public async Task<IActionResult> GetUser(int id)
{
var result = await _userService.GetByIdAsync(id);
return result switch
{
HttpResult<User>.Ok(var user) => Ok(user),
HttpResult<User>.NotFound(var msg) => NotFound(msg),
HttpResult<User>.Unauthorized() => Unauthorized(),
HttpResult<User>.InternalError(var msg) => StatusCode(500, msg),
_ => StatusCode(500, "Unexpected error")
};
}
Bu makalede kullanılan bilgiler aşağıdaki güvenilir kaynaklardan alınmıştır:
Alesta Web olarak tüm bilgileri doğruladık ve test ettik.
Discriminated Union, C#'ın uzun süredir beklediği güçlü bir özellik. Alesta Web olarak bu özelliğin C# 15 ile Kasım 2026'da gelmesini heyecanla bekliyoruz. O zamana kadar OneOf veya Thinktecture kütüphaneleri ile benzer işlevselliği elde edebilirsiniz.
Hızlı Özet / Quick Summary:
Faydalı Linkler / Useful Links:
© 2026 AlestaWeb - Tüm hakları saklıdır.