C# 2026: field ve Null-Conditional Assignment ile Güvenli Modeller

C# 14 field anahtar sözcüğüyle doğrulanan özellikler oluşturun; ?. atamalarının ne zaman çalıştığını ve nerede kullanılmaması gerektiğini örneklerle öğrenin.

C# 2026: field ve Null-Conditional Assignment ile Güvenli Modeller — .NET 10 / C# 14
Sürüm notu: Bu rehberdeki “2026” bir yıl ifadesidir. Aşağıdaki iki dil özelliği C# 14 ile gelir; örnekler .NET 10 üzerinde çalışır.

Bir özellik atandığında boşlukları temizlemek, negatif fiyatı reddetmek veya isteğe bağlı bir nesneye not eklemek sıradan işlerdir. Fakat bu küçük işler, modellerde tekrar eden backing field’lara ve uzayan null kontrollerine dönüşebilir.

C# 14 iki faydalı araç sunuyor: özellik erişimcilerinde field kullanımı ve atamanın sol tarafında null-conditional erişim. İkisi de kodu sadeleştirir; ancak doğrulama sorumluluğunu ortadan kaldırmaz.

Bu yazıda bir ürün modeli ve sipariş taslağı üzerinden hem normal akışı hem de hatalı girdilerin davranışını ele alacağız.

1. field tam olarak neyi değiştiriyor?

Klasik yaklaşımda doğrulama yapan bir özellik için özel bir alan tanımlarız:

private string _name = "Adsız ürün";

public string Name
{
    get => _name;
    set
    {
        if (string.IsNullOrWhiteSpace(value))
            throw new ArgumentException("Ürün adı boş olamaz.", nameof(value));

        _name = value.Trim();
    }
}

C# 14’te derleyicinin oluşturduğu backing field’a, erişimci içindeki field anahtar sözcüğüyle ulaşabiliriz. Böylece yalnızca alan bildirimini ve onu döndüren basit getter’ı kaldırırız; iş kuralı aynı yerde görünür kalır.

Bu özellik, field adlı bir değişken oluşturmanız anlamına gelmez. Bağlama duyarlı anahtar sözcük, ilgili özelliğin derleyici tarafından üretilen depolama alanını ifade eder.

2. Doğrulanan bir ürün modeli

.NET 10 konsol projesindeki ayrı bir Product.cs dosyası şöyle olabilir:

namespace CSharp2026.SafeModels;

public sealed class Product
{
    public string Name
    {
        get;
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("Ürün adı boş olamaz.", nameof(value));

            field = value.Trim();
        }
    } = "Adsız ürün";

    public decimal Price
    {
        get;
        set => field = value >= 0
            ? value
            : throw new ArgumentOutOfRangeException(nameof(value), "Fiyat negatif olamaz.");
    }
}

Burada iki kural var: ürün adı boş bırakılamaz ve fiyat negatif olamaz. Örneğin:

using CSharp2026.SafeModels;

var product = new Product
{
    Name = "  Klavye  ",
    Price = 1250m
};

Console.WriteLine(product.Name);  // Klavye
Console.WriteLine(product.Price); // 1250

get; otomatik getter’dır. Setter ise değeri doğrulayıp field alanına yazar. Getter ve setter’ı tek bir özellik içinde okuyarak saklanan değerin nereden geldiğini anlayabiliriz.

Başlangıç değerlerine de dikkat edin. Name için geçerli bir varsayılan metin verdik; Price için decimal’in varsayılanı olan sıfır geçerlidir. Property initializer, setter’ı çağırarak doğrulama yapmaz; backing field’ı başlatır. Bu nedenle başlangıç değerini de sizin doğru seçmeniz gerekir.

Gerçek uygulamanızda ücretsiz ürün kabul edilmiyorsa value >= 0 yeterli değildir. Para birimi, hassasiyet ve yuvarlama gibi finansal kurallar da bu basit örneğin dışındadır. Kısa sözdizimi, domain modelinin tamamlandığı anlamına gelmez.

3. Hatalı atama mevcut değeri bozmamalı

Doğrulamayı field atamasından önce yapmak önemlidir:

try
{
    product.Price = -1m;
}
catch (ArgumentOutOfRangeException)
{
    Console.WriteLine(product.Price); // 1250
}

Setter önce koşulu kontrol eder. Geçersiz değer geldiğinde istisna oluşur, dolayısıyla eski fiyat korunur. Aynı davranış boş ürün adı için de geçerlidir.

Bu, testlerde özellikle kontrol edilmesi gereken bir ayrıntıdır. Sadece “istisna fırlattı mı?” sorusu yetmez; nesnenin hatadan sonra hangi durumda kaldığını da doğrulamalısınız.

Setter içine uzak servis çağrısı, dosya erişimi veya kapsamlı iş akışı koymak ise hâlâ kötü bir sürpriz yaratabilir. Bir özelliği atamak ucuz ve öngörülebilir olmalıdır. Daha karmaşık işlemler için niyeti açık bir metot tercih edin.

4. Null-conditional assignment nasıl çalışır?

Sipariş taslağı modelimiz:

namespace CSharp2026.SafeModels;

public sealed class OrderDraft
{
    public string? Note { get; set; }
    public int Quantity { get; set; }
}

C# 14 öncesinde isteğe bağlı bir taslağa not atarken çoğunlukla if (draft is not null) bloğu yazardık. Artık ?. atamanın sol tarafında kullanılabilir:

OrderDraft? draft = null;
int calls = 0;

draft?.Note = BuildNote();
Console.WriteLine($"Null taslakta çağrı sayısı: {calls}");

draft = new OrderDraft();
draft?.Note = BuildNote();
draft?.Quantity += 1;
Console.WriteLine($"Dolu taslakta çağrı sayısı: {calls}");
Console.WriteLine($"Adet: {draft?.Quantity}");

string BuildNote()
{
    calls++;
    return "Kapıya teslim";
}

Beklenen çıktı:

Null taslakta çağrı sayısı: 0
Dolu taslakta çağrı sayısı: 1
Adet: 1

Buradaki kritik nokta, yalnızca atamanın atlanması değildir. Alıcı null olduğunda sağ taraftaki ifade de değerlendirilmez. İlk satırdaki BuildNote() hiç çağrılmaz. Bu davranış, not üretmenin pahalı olduğu veya başka bir yan etki oluşturduğu durumlarda önem kazanır.

Bileşik atamalar da desteklenir: draft?.Quantity += 1 kullanılabilir. Fakat draft?.Quantity++ ve draft?.Quantity-- bu özelliğin desteklediği biçimler değildir. Örnekleri birbirine eşdeğer sanarak değiştirmeyin.

5. Kısa yazmak her zaman doğru davranış değildir

Null-conditional assignment, null olduğunda hiçbir şey yapılmaması gerçekten doğruysa uygundur. İsteğe bağlı bir ekran ayarı, henüz oluşmamış bir önizleme nesnesi veya yardımcı görüntüleme durumu buna örnek olabilir.

Fakat sipariş tamamlama gibi bir işlemde taslağın bulunması zorunluysa, sessizce atlamak hatayı saklar. Böyle bir durumda açık kontrol daha doğru olur:

ArgumentNullException.ThrowIfNull(draft);
draft.Note = "Onay için hazır";

Ayrıca ?. thread safety sağlamaz. Paylaşılan nesnede Quantity += 1 işlemini atomik hâle getirmez; eşzamanlılık için kilit, uygun atomik işlemler veya farklı bir durum yönetimi yaklaşımı gerekebilir.

Nullable reference types da bu resmin sadece bir parçasıdır. Anotasyonlar, derleyicinin olası null kullanımını raporlamasına yardımcı olur; dış sistemlerden gelen hatalı verilerin var olmayacağını garanti etmez. Giriş sınırlarında doğrulama yapmayı sürdürün.

6. Projeye taşımadan önce kontrol listesi

  • Hedef çerçeveniz net10.0, dil sürümünüz C# 14 mü?
  • Varsayılan özellik değerleri domain kurallarına uygun mu?
  • Geçersiz atama sonrasında eski değer korunuyor mu?
  • Null durumunda işlemin atlanması gerçekten istenen davranış mı?
  • Sınıfınızda zaten field adlı bir üye varsa anlam karışıklığı oluşuyor mu?

Son maddede gerekirse mevcut üyeyi yeniden adlandırın. @field veya this.field, mevcut bir üye ile yeni anahtar sözcüğü ayırt etmeye yardımcı olabilir. Bu özellikle eski kodları yeni derleyiciyle derlerken gözden geçirilmelidir.

Sonuç: sade kod, açık kurallar

field, doğrulanan özelliklerde gereksiz alan tekrarını azaltır. Null-conditional assignment ise isteğe bağlı nesneler için kontrol akışını sadeleştirir. İkisinin de değeri, kodun niyetini görünür kılmalarıdır; zorunlu kontrolleri gizlemeleri değil.

Çalıştırılabilir kod örneklerini indir — ZIP. SafeModels projesi ad temizleme, geçersiz girdiler, eski değerin korunması ve null atamalardaki yan etkiler için on doğrulama içerir.

Kaynaklar ve devamı

Bu yazı yapay zekâ desteğiyle hazırlanmıştır. İndirilebilir örnekler .NET SDK 10.0.401 üzerinde, C# 14 ayarı ve uyarıları hata kabul eden yapılandırmayla derlenip çalıştırılmıştır.