null berarti "tidak ada nilai". Reference type (class, string, array) sejak dulu bisa menyimpannya. Value type (int, decimal, bool, DateTime, struct) tidak bisa, kecuali kamu membuatnya nullable dengan ?. C# lalu memberimu sekumpulan kecil operator untuk bekerja dengan nilai yang mungkin tidak ada: ??, ??= dan ?..
Nullable value type: int?
Tambahkan ? pada value type untuk mengizinkan null. int? adalah cara singkat untuk struct Nullable<int>, yang menyimpan sebuah nilai ditambah penanda apakah nilai itu ada:
Output:
False
True
True
5.50
0
3
Rated 4 stars
4
Sumber umum nullable value type adalah field formulir yang opsional, parameter query, dan kolom NULL di database: sebuah Order dengan DateTime? ShippedAt belum dikirim selama nilainya null. Alternatifnya, nilai ajaib seperti -1 atau DateTime.MinValue, terlihat seperti data sungguhan dan cepat atau lambat akan dipakai sebagai data sungguhan.
int? hanya bisa dikonversi ke int secara eksplisit, karena konversinya bisa gagal. Ini membawa kita ke satu-satunya exception yang dilempar nullable value type.
"Nullable object must have a value"
Membaca .Value saat tidak ada nilai, atau melakukan cast int? yang null ke int, akan melempar InvalidOperationException:
Output:
InvalidOperationException: Nullable object must have a value.
0
0
not stocked
stock is int count adalah sebuah pola: ia memeriksa apakah ada nilai dan membukanya ke dalam count dalam satu langkah, yang lebih enak dibaca daripada HasValue ditambah Value. Halaman pattern matching berisi bentuk-bentuk lainnya.
Aritmetika dan perbandingan dengan null
Operator pada nullable value type bersifat lifted: kalau salah satu operand null, hasilnya null. Perbandingan urutan (<, >, <=, >=) adalah pengecualiannya: hasilnya false, bukan null. (Pengecualian lainnya adalah bool? dengan & dan |, di mana null & false bernilai false.) Contohnya:
Output:
True
[]
False
False
True
True
a > 5 dan a <= 5 sama-sama false, jadi !(a > 5) tidak sama dengan a <= 5 saat a bernilai null. Kode seperti if (!(age >= 18)) Deny(); memperlakukan umur yang tidak ada sebagai di bawah umur, yang mungkin sesuai atau tidak dengan maksudmu. Tentukan dulu apa arti null, biasanya dengan ??, lalu bandingkan.
Operator null-coalescing ??
a ?? b bernilai a saat a tidak null, dan b kalau sebaliknya. Operator ini bekerja pada nullable value type dan reference type, dan bisa dirangkai dari kiri ke kanan:
Output:
dark
16px
20
ANONYMOUS
Sisi kanannya hanya dievaluasi saat dibutuhkan, jadi cache ?? LoadFromDatabase() tidak mengakses database saat cache-nya punya nilai. ?? juga menerima ekspresi throw, yang menghasilkan guard yang ringkas (lihat throw):
this.repository = repository ?? throw new ArgumentNullException(nameof(repository));
??= (C# 8)
x ??= y memberikan y ke x hanya kalau x bernilai null. Ini adalah idiom untuk lazy initialization:
private List<string> tags;
public void AddTag(string tag)
{
tags ??= new List<string>(); // C# 8 and later
tags.Add(tag);
}
Sebelum C# 8, tulis tags = tags ?? new List<string>(); atau if (tags == null) tags = new List<string>();.
Operator null-conditional ?.
a?.Member mengevaluasi Member hanya kalau a tidak null; kalau tidak, seluruh ekspresinya bernilai null. Operator ini mengubah rangkaian pemeriksaan null menjadi satu ekspresi:
Output:
Braga
(no city)
(no customer)
0
True
handler: saved
Detail yang perlu kamu tahu:
- Operator ini melakukan short-circuit pada sisa rangkaiannya: di
nobody?.Address?.City, begitunobodynull, tidak ada yang dijalankan di sebelah kanannya. - Saat member-nya mengembalikan value type, hasilnya menjadi nullable:
Orders?.Countbertipeint?, itulah sebabnya ia butuh?? 0untuk menjadiintlagi. ?.tidak bisa menjadi tujuan pemberian nilai sebelum C# 14:customer?.Name = "x"tidak bisa di-compile di versi sebelumnya.handler?.Invoke(...)adalah cara standar untuk memicu sebuah event atau memanggil callback yang opsional.
Pakai ?. di tempat null memang keadaan yang sah. Menaburkannya di mana-mana untuk menghindari exception menyembunyikan bug yang menghasilkan null tak terduga itu, dan program berjalan terus dengan data yang hilang alih-alih gagal di tempat masalahnya berada.
Memeriksa null
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 selalu melakukan pemeriksaan null yang sebenarnya, sedangkan == memanggil overload operator milik tipe itu kalau ada, jadi overload yang ditulis dengan buruk bisa membuat x == null berbohong. Untuk argumen yang tidak boleh null, gagalkan sejak awal:
public void Ship(Order order)
{
ArgumentNullException.ThrowIfNull(order); // .NET 6+
// ...
}
Nullable reference type (C# 8)
Menambahkan ? pada value type mengubah tipenya. Menambahkan ? pada reference type, sejak C# 8, hanya mengubah apa yang diperiksa compiler. Saat fitur ini diaktifkan (<Nullable>enable</Nullable> di file proyek, default untuk proyek baru sejak .NET 6), string berarti "seharusnya tidak pernah null" dan string? berarti "mungkin null", dan compiler memberi warning saat kodenya tidak sesuai:
#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
Compiler mengikuti pemeriksaanmu di sepanjang method: setelah if (input != null), input dianggap tidak null. Operator ! (null-forgiving) membungkam sebuah warning saat kamu lebih tahu daripada analisis compiler; setiap pemakaiannya adalah klaim yang tidak bisa diverifikasi compiler, jadi jarang-jarang saja memakainya.
Ada dua fakta yang menjernihkan sebagian besar kebingungan. Pertama, fitur ini hanya berupa warning: program berjalan dengan cara yang sama, dan sebuah string tetap bisa berisi null saat program berjalan kalau nilainya berasal dari kode tanpa anotasi. Kedua, string? bukan Nullable<string>; tidak ada HasValue padanya.
Kesalahan yang sering terjadi
.Valuetanpa pemeriksaan. MelemparInvalidOperationExceptionsaat null. Pakai??,GetValueOrDefault()atauis int n.- Mengira
!(x > 5)berartix <= 5. Tidak saatxbernilai null: setiap perbandingan urutan dengan null bernilai false. - Menutupi setiap null dengan
?.. Cara ini membungkam gejalanya dan memindahkan kegagalan makin jauh dari penyebabnya. - Menganggap warning nullable reference sebagai gangguan. Setiap warning menandai baris yang bisa melempar
NullReferenceException. Perbaiki alurnya atau nyatakan maksudmu dengan?. - Nilai ajaib sebagai ganti null.
-1,0danDateTime.MinValueuntuk "tidak diketahui" terlihat seperti data sungguhan. Pakaiint?danDateTime?.
Pertanyaan yang sering diajukan
Apa arti int? di C#?
int? adalah cara singkat untuk Nullable<int>: sebuah int yang juga bisa bernilai null. Value type seperti int, decimal, bool dan DateTime biasanya selalu berisi nilai; menambahkan ? memberinya keadaan "tidak ada nilai", yang dibutuhkan untuk field formulir yang tidak diisi atau kolom database yang NULL. Periksa dengan HasValue atau != null dan baca dengan Value atau GetValueOrDefault().
Apa yang dilakukan ?? di C#?
a ?? b adalah operator null-coalescing: hasilnya a kalau a tidak null, dan b kalau sebaliknya. string name = input ?? "guest"; memberi nilai cadangan dalam satu ekspresi. Operator ini bisa dirangkai (a ?? b ?? c mengambil nilai pertama yang tidak null) dan b hanya dievaluasi saat dibutuhkan. ??= (C# 8) memberikan nilai sisi kanan hanya saat variabelnya null.
Apa yang dilakukan ?. di C#?
a?.B adalah operator null-conditional: kalau a null, seluruh ekspresinya bernilai null dan B tidak pernah dievaluasi, jadi tidak ada NullReferenceException. Operator ini bisa dirangkai (order?.Customer?.Address?.City) dan bekerja untuk pemanggilan method (logger?.Log(...)), event (Changed?.Invoke(...)) dan indexer (items?[0]). Gabungkan dengan ?? untuk nilai default: order?.Customer?.Name ?? "unknown".
Apa penyebab "Nullable object must have a value"?
Membaca .Value dari nullable value type yang null, atau melakukan cast ke tipe yang tidak nullable ((int)maybe), akan melempar InvalidOperationException dengan pesan itu. Periksa HasValue terlebih dahulu, atau pakai GetValueOrDefault(), ?? fallback, atau sebuah pola: if (maybe is int n) { ... }.
Bagaimana cara memeriksa null di C#?
x == null dan x is null sama-sama bisa dipakai; is null (C# 7) tidak bisa dipengaruhi oleh tipe yang meng-overload ==, jadi banyak codebase lebih memilihnya. Kebalikannya adalah x != null, atau x is not null di C# 9. Untuk menolak argumen null, pakai ArgumentNullException.ThrowIfNull(x) (.NET 6) atau _ = x ?? throw new ArgumentNullException(nameof(x)); di kode yang lebih lama.
Apa itu nullable reference type di C#?
Fitur compiler dari C# 8, yang diaktifkan dengan <Nullable>enable</Nullable> (default di proyek baru .NET 6 ke atas). Saat fitur ini aktif, string berarti "tidak pernah null" dan string? berarti "mungkin null", dan compiler memberi warning saat kamu mungkin mengakses null (CS8602) atau memberikan null ke variabel yang tidak nullable (CS8600). Fitur ini hanya mengubah warning, bukan perilaku saat program berjalan.