C# Discriminated Union Rehberi: Pattern Matching ve OneOf Kütüphanesi (2026)

03.02.2026 10:55 Haber

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 Nedir? (What is Discriminated Union?)

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.

Günlük Hayat Örneği / Real Life Example:

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!

? Alesta Web Bilgi:

Discriminated union, "tagged union", "sum type" veya "variant" olarak da bilinir. Hepsi aynı konsepti ifade eder (they all describe the same concept).

C#'ta Discriminated Union Tarihi (History in C#)

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.

⚠️ Önemli Not / Important Note:

Ş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# 14/15 Önerilen Söz Dizimi (Proposed Syntax)

C# 15'te beklenen discriminated union syntax'ı şöyle görünüyor:

Temel Syntax / Basic Syntax:

// 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!

API Result Örneği / API Result Example:

// 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..."
};
✅ Exhaustive Pattern Matching:

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)!

Pattern Matching ile Kullanım (Usage with Pattern Matching)

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:

Switch Expression ile / With Switch Expression:

// Ö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"
    };
}

Guard Clause ile / With Guard Clause:

// 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"
};
? Alesta Web İpucu:

Pattern matching'de _ (discard) kullanarak kullanmadığınız değerleri atlayabilirsiniz. Bu kod okunurluğunu artırır (improves code readability).

Bugün Nasıl Kullanılır? (How to Use Today?)

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:

Yöntem 1: Abstract Record ile Simülasyon

Manuel Discriminated Union / Manual Implementation:

// 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
};
⚠️ Dikkat / Warning:

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.

Yöntem 2: Interface ile Marker Pattern

Interface Marker Pattern:

// 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"
};

Popüler Kütüphaneler (Popular Libraries)

C#'ta discriminated union benzeri işlevsellik sağlayan birkaç popüler kütüphane var. Alesta Web olarak en çok kullanılanları inceledik:

1. OneOf Kütüphanesi (En Popüler)

OneOf Kurulum ve Kullanım / Installation and Usage:

// 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!
✅ OneOf Avantajı:

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.

2. Thinktecture.Runtime.Extensions

Thinktecture Kullanımı / Thinktecture Usage:

// 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}"
);

3. LanguageExt (Functional Programming)

LanguageExt ile Fonksiyonel Yaklaşım:

// 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

Avantajları ve Kullanım Alanları (Benefits and Use Cases)

Discriminated union neden bu kadar önemli? Alesta Web olarak en önemli avantajları listeleyelim:

1. Type Safety (Tip Güvenliği)

Her case kendi tipine sahip. Yanlış tipe erişmeye çalıştığınızda compiler sizi uyarır.

2. Exhaustive Pattern Matching

Tüm olası durumları handle etmenizi zorunlu kılar. Bir case'i unutursanız derleme hatası alırsınız.

3. Null Reference Prevention

null yerine explicit "NotFound" veya "Empty" case'i kullanabilirsiniz. NullReferenceException riskini azaltır.

4. Self-Documenting Code

Kod kendini açıklar. Bir fonksiyonun döndürebileceği tüm durumlar union tanımında görünür.

Yaygın Kullanım Alanları / Common Use Cases:

// 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) }
? Alesta Web Tavsiyesi:

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).

Pratik Örnekler (Practical Examples)

Alesta Web olarak gerçek dünya senaryolarından örnekler paylaşalım:

Örnek 1: E-Ticaret Ödeme Sistemi / E-Commerce Payment System:

// 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)
);

Örnek 2: JSON API Response Handler:

// 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")
    };
}

Kaynaklar ve Referanslar / Sources and References

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.

Sonuç (Conclusion)

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:

  • ✅ Discriminated union = "A veya B veya C" tipi
  • ✅ C# 15'te native support bekleniyor (Kasım 2026)
  • ✅ Exhaustive pattern matching ile güvenli kod
  • ✅ Bugün OneOf veya Thinktecture kullanılabilir
  • ✅ API response, validation, state machine için ideal

Faydalı Linkler / Useful Links:

© 2026 AlestaWeb - Tüm hakları saklıdır.

WM Tools
💫

WebMaster Tools

15 Profesyonel Araç