Menu

C# Nullable: int?, ?? Null Birleştirme, ?. ve Null Kontrolleri

C#'ta null hakkında her şey: HasValue, Value ve GetValueOrDefault ile nullable değer tipleri (int?, Nullable<T>), null birleştirme operatörleri ?? ve ??=, null koşullu operatör ?., null kontrolleri ve C# 8'deki nullable referans tipleri (string?).

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

null "değer yok" demektir. Referans tipleri (sınıflar, string'ler, diziler) onu her zaman tutabilmiştir. Değer tipleri (int, decimal, bool, DateTime, struct'lar) ise onları ? ile nullable yapmadıkça tutamaz. C# bundan sonra size eksik olabilecek değerlerle çalışmak için küçük bir operatör kümesi verir: ??, ??= ve ?..

Nullable değer tipleri: int?

null'a izin vermek için bir değer tipine ? ekleyin. int?, bir değer ve değerin var olup olmadığını söyleyen bir bayrak tutan Nullable<int> struct'ının kısaltmasıdır:

Çıktı:

False
True
True
5.50
0
3
Rated 4 stars
4

Nullable değer tiplerinin tipik kaynakları isteğe bağlı form alanları, sorgu parametreleri ve bir veritabanındaki NULL sütunlardır: DateTime? ShippedAt'e sahip bir Order, değer null olduğu sürece gönderilmemiştir. Alternatif olan -1 ya da DateTime.MinValue gibi sihirli bir değer gerçek veri gibi görünür ve er ya da geç öyle kullanılır.

Bir int? yalnızca açıkça int'e dönüşür, çünkü dönüşüm başarısız olabilir. Bu da bizi nullable değer tiplerinin fırlattığı tek istisnaya getirir.

"Nullable object must have a value"

Değer yokken .Value'yu okumak ya da null bir int?'yi int'e cast etmek InvalidOperationException fırlatır:

Çıktı:

InvalidOperationException: Nullable object must have a value.
0
0
not stocked

stock is int count bir desendir: bir değeri test eder ve tek adımda count'a açar; bu, HasValue artı Value'dan daha iyi okunur. Pattern matching sayfasında daha fazla biçim vardır.

null ile aritmetik ve karşılaştırmalar

Nullable değer tiplerindeki operatörler yükseltilmiştir (lifted): işlenenlerden biri null ise sonuç null'dır. Sıralama karşılaştırmaları (<, >, <=, >=) istisnadır: null yerine false döndürürler. (Diğer istisna & ve | ile bool?'dir; null & false false'tur.) Örneğin:

Çıktı:

True
[]
False
False
True
True

a > 5 ve a <= 5 ikisi de false'tur, bu yüzden a null olduğunda !(a > 5), a <= 5 ile aynı şey değildir. if (!(age >= 18)) Deny(); gibi bir kod eksik bir yaşı reşit olmamak olarak ele alır; bu kastettiğiniz şey olabilir de olmayabilir de. Önce null'ın ne anlama gelmesi gerektiğine karar verin, genellikle ?? ile, sonra karşılaştırın.

Null birleştirme operatörü ??

a ?? b, a null değilse a'ya, aksi halde b'ye değerlendirilir. Nullable değer tiplerinde ve referans tiplerinde çalışır ve soldan sağa zincirlenir:

Çıktı:

dark
16px
20
ANONYMOUS

Sağ taraf yalnızca gerektiğinde değerlendirilir, bu yüzden önbellekte bir değer varken cache ?? LoadFromDatabase() veritabanına gitmez. ?? bir throw ifadesini de kabul eder, bu da kompakt bir koruma sağlar (bkz. throw):

this.repository = repository ?? throw new ArgumentNullException(nameof(repository));

??= (C# 8)

x ??= y, yalnızca x null ise x'e y'yi atar. Tembel başlatmanın deyimidir:

private List<string> tags;

public void AddTag(string tag)
{
    tags ??= new List<string>();   // C# 8 and later
    tags.Add(tag);
}

C# 8'den önce tags = tags ?? new List<string>(); ya da if (tags == null) tags = new List<string>(); yazın.

Null koşullu operatör ?.

a?.Member, Member'ı yalnızca a null değilse değerlendirir; aksi halde tüm ifade null olur. Bir null kontrolleri zincirini tek bir ifadeye çevirir:

Çıktı:

Braga
(no city)
(no customer)
0
True
handler: saved

Bilmeye değer ayrıntılar:

  • Operatör zincirin geri kalanını kısa devre yapar: nobody?.Address?.City'de nobody null olduğu anda sağdaki hiçbir şey çalışmaz.
  • Üye bir değer tipi döndürdüğünde sonuç nullable olur: Orders?.Count int?'dir; tekrar int olması için ?? 0 gerektirmesinin nedeni budur.
  • C# 14'ten önce ?. bir atamanın hedefi olamaz: customer?.Name = "x" önceki sürümlerde derlenmez.
  • handler?.Invoke(...) bir event'i tetiklemenin ya da isteğe bağlı bir callback'i çağırmanın standart yoludur.

?.'yı null'ın meşru bir durum olduğu yerde kullanın. İstisnalardan kaçınmak için onu her yere serpiştirmek beklenmeyen null'ı üreten hatayı gizler ve program, sorunun olduğu yerde başarısız olmak yerine eksik veriyle devam eder.

Null kontrolü

if (customer == null) { ... }             // classic
if (customer is null) { ... }             // C# 7 constant pattern
if (customer != null) { ... }
if (customer is not null) { ... }         // C# 9
if (customer is { } c) { ... }            // C# 8 property pattern: not null, and named

is null her zaman gerçek bir null kontrolü yapar, == ise tipin bir operatör aşırı yüklemesi varsa onu çağırır; bu yüzden kötü yazılmış bir aşırı yükleme x == null'ın yalan söylemesine yol açabilir. Null olmaması gereken argümanlar için erken başarısız olun:

public void Ship(Order order)
{
    ArgumentNullException.ThrowIfNull(order);   // .NET 6+
    // ...
}

Nullable referans tipleri (C# 8)

Bir değer tipine ? eklemek tipi değiştirir. C# 8'den beri bir referans tipine ? eklemek ise yalnızca derleyicinin neyi kontrol ettiğini değiştirir. Özellik açıkken (proje dosyasında <Nullable>enable</Nullable>, .NET 6'dan beri yeni projelerde varsayılan) string "asla null olmamalı", string? ise "null olabilir" demektir ve kod buna uymadığında derleyici uyarır:

#nullable enable

public class User
{
    public string Email { get; set; }      // warning CS8618: Non-nullable property 'Email'
                                           // must contain a non-null value when exiting constructor.
    public string? Nickname { get; set; }  // allowed to be null

    public int NicknameLength() => Nickname.Length;
    // warning CS8602: Dereference of a possibly null reference.

    public int SafeLength() => Nickname?.Length ?? 0;   // no warning
}

string? input = Console.ReadLine();
string name = input;                       // warning CS8600: Converting null literal or possible
                                           // null value to non-nullable type.
string checkedName = input ?? "guest";     // no warning
int len = input!.Length;                   // ! tells the compiler "trust me": no warning

Derleyici kontrollerinizi metot boyunca izler: if (input != null)'dan sonra input null değil sayılır. ! (null bağışlayan) operatörü, siz analizden daha iyi bildiğinizde bir uyarıyı susturur; her biri derleyicinin doğrulayamadığı bir iddiadır, bu yüzden onları seyrek tutun.

Kafa karışıklığının çoğunu gideren iki gerçek. Birincisi, bu yalnızca uyarılardır: program aynı şekilde çalışır ve bir string, işaretlemesi olmayan koddan geldiyse çalışma zamanında yine null tutabilir. İkincisi, string? Nullable<string> değildir; üzerinde HasValue yoktur.

Yaygın hatalar

  • Kontrolsüz .Value. Null olduğunda InvalidOperationException fırlatır. ??, GetValueOrDefault() ya da is int n kullanın.
  • !(x > 5)'in x <= 5 anlamına geldiğini varsaymak. x null olduğunda değil: null ile her sıralama karşılaştırması false'tur.
  • Her null'ı ?. ile örtmek. Belirtiyi susturur ve başarısızlığı nedeninden daha uzağa taşır.
  • Nullable referans uyarılarını gürültü saymak. Her biri NullReferenceException fırlatabilecek bir satırı işaretler. Akışı düzeltin ya da niyeti ? ile belirtin.
  • null yerine sihirli değerler. "Bilinmiyor" için -1, 0 ve DateTime.MinValue gerçek veri gibi görünür. int? ve DateTime? kullanın.

Sıkça Sorulan Sorular

C#'ta int? ne anlama gelir?

int?, Nullable<int>'in kısaltmasıdır: null da olabilen bir int. int, decimal, bool ve DateTime gibi değer tipleri normalde her zaman bir değer tutar; ? eklemek onlara bir "değer yok" durumu verir; eksik bir form alanının ya da NULL bir veritabanı sütununun ihtiyaç duyduğu budur. Onu HasValue ya da != null ile kontrol edin, Value ya da GetValueOrDefault() ile okuyun.

C#'ta ?? ne yapar?

a ?? b null birleştirme operatörüdür: a null değilse a'ya, aksi halde b'ye değerlendirilir. string name = input ?? "guest"; tek bir ifadede bir yedek sağlar. Zincirlenebilir (a ?? b ?? c null olmayan ilk değeri alır) ve b yalnızca gerektiğinde değerlendirilir. ??= (C# 8) sağ tarafı yalnızca değişken null olduğunda atar.

C#'ta ?. ne yapar?

a?.B null koşullu operatördür: a null ise tüm ifade null olur ve B hiç değerlendirilmez, bu yüzden NullReferenceException olmaz. Zincirlenebilir (order?.Customer?.Address?.City) ve metot çağrıları (logger?.Log(...)), event'ler (Changed?.Invoke(...)) ve indeksleyiciler (items?[0]) için çalışır. Bir varsayılan için onu ?? ile birleştirin: order?.Customer?.Name ?? "unknown".

"Nullable object must have a value" hatasına ne yol açar?

Null olan bir nullable değer tipinin .Value'sunu okumak ya da onu nullable olmayan tipe cast etmek ((int)maybe) bu mesajla InvalidOperationException fırlatır. Önce HasValue'yu kontrol edin ya da GetValueOrDefault(), ?? fallback veya bir desen kullanın: if (maybe is int n) { ... }.

C#'ta null kontrolü nasıl yapılır?

x == null ve x is null ikisi de çalışır; is null (C# 7) =='i aşırı yükleyen bir tipten etkilenemez, bu yüzden birçok kod tabanı onu tercih eder. Tersi x != null ya da C# 9'da x is not null'dır. Null argümanları reddetmek için ArgumentNullException.ThrowIfNull(x) (.NET 6) ya da eski kodda _ = x ?? throw new ArgumentNullException(nameof(x)); kullanın.

C#'ta nullable referans tipleri nedir?

<Nullable>enable</Nullable> ile açılan bir C# 8 derleyici özelliğidir (yeni .NET 6+ projelerinde varsayılan). Açıkken string "asla null değil", string? ise "null olabilir" demektir ve derleyici null'a erişebileceğiniz yerlerde (CS8602) ya da nullable olmayan bir değişkene null atadığınızda (CS8600) uyarır. Yalnızca uyarıları değiştirir, çalışma zamanı davranışını değiştirmez.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA