Memahami nullability

Selesai

Jika Anda adalah pengembang .NET, kemungkinan Anda telah menemukan System.NullReferenceException. Ini terjadi pada run time ketika null dereferensi; yaitu, ketika variabel dievaluasi pada runtime, tetapi variabel mengacu pada null. Pengecualian ini sejauh ini adalah pengecualian yang paling umum terjadi dalam ekosistem .NET. Pembuat null, Sir Tony Hoare, menyebut null sebagai "kesalahan miliaran dolar."

Dalam contoh berikut, variabel FooBar ditetapkan ke null dan segera didereferensikan, sehingga menunjukkan masalah:

// Declare variable and assign it as null.
FooBar fooBar = null;

// Dereference variable by calling ToString.
// This will throw a NullReferenceException.
_ = fooBar.ToString();

// The FooBar type definition.
record FooBar(int Id, string Name);

Masalahnya menjadi jauh lebih rumit untuk dikenali sebagai pengembang ketika aplikasi Anda tumbuh dalam ukuran dan kompleksitas. Menemukan potensi kesalahan seperti ini adalah pekerjaan untuk alat, dan pengompilasi C# ada di sini untuk membantu.

Menentukan keamanan null

Istilah keamanan null mendefinisikan serangkaian fitur khusus untuk jenis nullable yang membantu mengurangi jumlah kemungkinan NullReferenceException kemunculan.

Mempertimbangkan contoh sebelumnya FooBar , Anda dapat menghindari NullReferenceException dengan memeriksa apakah fooBar variabel sebelum null mendereferensikannya:

// Declare variable and assign it as null.
FooBar fooBar = null;

// Check for null
if (fooBar is not null)
{
    _ = fooBar.ToString();
}

// The FooBar type definition for example.
record FooBar(int Id, string Name);

Untuk membantu dalam mengidentifikasi skenario seperti ini, pengompilasi dapat menyimpulkan niat kode Anda dan menegakkan perilaku yang diinginkan. Namun, ini hanya ketika konteks yang dapat bernilai null diaktifkan. Sebelum membahas konteks nullable, mari kita jelaskan kemungkinan jenis nullable.

Jenis yang dapat diubah ke null

Sebelum C# 2.0, hanya jenis referensi yang dapat diubah ke null. Jenis nilai seperti int atau DateTimetidak bisa menjadi null. Jika jenis ini diinisialisasi tanpa nilai, jenis ini akan kembali ke nilai default-nya. Dalam kasus int, ini adalah 0. Untuk DateTime, itu adalah DateTime.MinValue.

Jenis referensi yang dibuat tanpa nilai awal bekerja secara berbeda. Nilai default untuk semua jenis referensi adalah null.

Pertimbangkan cuplikan kode C# berikut:

string first;                  // first is null
string second = string.Empty   // second is not null, instead it's an empty string ""
int third;                     // third is 0 because int is a value type
DateTime date;                 // date is DateTime.MinValue

Dalam contoh sebelumnya:

  • first adalah null karena jenis referensi string dideklarasikan tetapi tidak ada penugasan yang dibuat.
  • second ditetapkan string.Empty ketika dideklarasikan. Objek tidak pernah memiliki penugasan null.
  • third meskipun 0 tidak ditetapkan. Ini adalah struct (jenis nilai) dan memiliki nilai default0.
  • date tidak diinisialisasi, tetapi nilainya default adalah System.DateTime.MinValue.

Dimulai dengan C# 2.0, Anda dapat menentukan jenis nilai nullable menggunakan Nullable<T> (atau T? untuk shorthand). Ini memungkinkan jenis nilai menjadi jenis nilai yang dapat diubah ke null. Pertimbangkan cuplikan kode C# berikut:

int? first;            // first is implicitly null (uninitialized)
int? second = null;    // second is explicitly null
int? third = default;  // third is null as the default value for Nullable<Int32> is null
int? fourth = new();    // fourth is 0, since new calls the nullable constructor

Dalam contoh sebelumnya:

  • first adalah null karena jenis nilai yang dapat diubah ke null batal diinisialisasi.
  • second ditetapkan null ketika dideklarasikan.
  • third adalah null seperti nilai default untuk Nullable<int> adalah null.
  • fourth adalah 0 seperti ekspresi new() memanggil konstruktor Nullable<int>, dan int adalah 0 secara default.

C# 8.0 memperkenalkan tipe referensi nullable, yang memungkinkan Anda menyampaikan maksud bahwa tipe referensi bisa jadinull atau selalu non-null. Anda mungkin berpikir, "Saya pikir semua jenis referensi nullable!" Anda tidak salah, dan mereka. Fitur ini memungkinkan Anda untuk mengekspresikan niat Anda, yang kemudian coba diberlakukan oleh pengkompilasi. Sintaks T? yang sama mengekspresikan bahwa jenis referensi dimaksudkan agar dapat diubah ke null.

Pertimbangkan cuplikan kode C# berikut:

#nullable enable

string first = string.Empty;
string second;
string? third;

Mengingat contoh sebelumnya, pengkompilasi menyimpulkan niat Anda sebagai berikut:

  • first tidak pernahnull karena sudah pasti ditetapkan.
  • second , meskipun awalnya null. Mengevaluasi second sebelum menetapkan nilai menghasilkan peringatan pengompilasi karena batal diinisialisasi.
  • third mungkin.null Misalnya, mungkin menunjuk ke System.String, tetapi mungkin menunjuk ke null. Salah satu variasi ini dapat diterima. Pengompilasi membantu Anda dengan memperingatkan Anda jika Anda melakukan dereferensi third tanpa terlebih dahulu memeriksa bahwa itu bukan null.

Penting

Untuk menggunakan fitur jenis referensi nullable seperti yang ditunjukkan di atas, fitur tersebut harus dalam konteks nullable. Hal ini dirincikan di bagian berikutnya.

Konteks yang dapat diubah ke null

Konteks yang dapat diubah ke null memungkinkan kontrol terperinci untuk bagaimana pengompilasi menafsirkan variabel jenis referensi. Ada empat kemungkinan konteks yang dapat diubah ke null:

  • disable: Pengompilasi berperilaku yang mirip dengan C# 7.3 dan yang lebih lama.
  • enable: Pengompilasi memungkinkan semua analisis referensi null dan semua fitur bahasa.
  • warnings: Pengompilasi melakukan semua analisis null dan memancarkan peringatan ketika kode mungkin melakukan dereferensi null.
  • annotations: Pengompilasi tidak melakukan analisis null atau memancarkan peringatan ketika kode mungkin melakukan dereferensi null, tetapi Anda masih dapat membuat anotasi kode Anda menggunakan jenis ? referensi yang dapat diubah ke null dan operator null-forgiving (!).

Modul ini dilingkup ke konteks disable atau enable nullable. Untuk informasi selengkapnya, lihat tipe referensi nullable: Konteks Nullable.

Mengaktifkan jenis referensi yang dapat diubah ke null

Dalam file proyek C# (.csproj), tambahkan simpul anak <Nullable> ke <Project> elemen (atau tambahkan ke yang sudah ada <PropertyGroup>). Ini akan menerapkan konteks yang dapat diubah ke null enable ke seluruh proyek.

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net6.0</TargetFramework>
        <Nullable>enable</Nullable>
    </PropertyGroup>

    <!-- Omitted for brevity -->

</Project>

Atau, Anda dapat menetapkan konteks nullable ke file C# menggunakan direktif kompilator.

#nullable enable

Direktif kompilator C# sebelumnya secara fungsional setara dengan konfigurasi proyek, tetapi dicakup ke file tempatnya berada. Untuk informasi selengkapnya, lihat Tipe referensi nullable: Konteks tipe referensi nullable (dokumentasi)

Penting

Konteks nullable diaktifkan dalam file .csproj secara default di semua templat proyek C# yang dimulai dengan .NET 6.0 dan yang lebih besar.

Saat konteks yang dapat diubah ke null diaktifkan, Anda akan mendapatkan peringatan baru. Pertimbangkan contoh sebelumnya FooBar , yang memiliki dua peringatan saat dianalisis dalam konteks nullable:

  1. Baris FooBar fooBar = null; memiliki peringatan pada null penugasan: C# Peringatan CS8600: Mengonversi nilai null literal atau kemungkinan nilai null ke tipe yang tidak bisa bernilai null.

    Cuplikan layar C# Peringatan CS8600: Mengonversi nilai-null literal atau kemungkinan nilai null ke jenis yang tidak dapat bernilai null.

  2. Garis _ = fooBar.ToString(); juga memiliki peringatan. Kali ini kompilator khawatir bahwa fooBar mungkin bernilai null: C# Peringatan CS8602: Dereferensi referensi yang mungkin null.

    Cuplikan layar C# Peringatan CS8602: Dereferensi referensi yang mungkin null.

Penting

Tidak ada jaminan keamanan null yang terjamin, bahkan jika Anda menanggapi dan menghilangkan semua peringatan. Ada beberapa skenario terbatas yang akan melewati analisis kompilator, namun menghasilkan runtime NullReferenceException.

Ringkasan

Dalam unit ini, Anda mempelajari cara mengaktifkan konteks yang dapat diubah ke null dalam C# untuk membantu melindungi dari NullReferenceException. Di unit berikutnya, Anda akan mempelajari lebih lanjut tentang mengekspresikan secara eksplisit niat Anda dalam konteks yang dapat diubah ke null.