Pada kode non-generik, tanda tanya memiliki makna tunggal: int? adalah singkatan Nullable<int>. Pada kode generik, makna T? tidak lagi tunggal, karena compiler belum tahu T adalah value type atau reference type. Bahasa C# lalu mengikat makna T? pada constraint yang menyertai deklarasi. Bab ini membedah tiga makna tersebut dan cara menandai hasil generik yang mungkin kosong.
Tiga Makna Anotasi T #
| Deklarasi | Makna T? |
|---|---|
where T : struct | Singkatan Nullable<T>, misalnya int? |
where T : class | T boleh berisi null (nullable reference type) |
| Tanpa constraint | T boleh berisi default(T) (sejak C# 9) |
Ketiga bentuk itu sama-sama mengumumkan “nilai mungkin kosong”, tetapi mekanisme runtime-nya berbeda: Nullable<T> adalah struct pembungkus sungguhan, sedangkan dua bentuk lainnya hanya anotasi untuk compiler.
Cache dengan Hasil Bisa Kosong #
Class cache menyimpan satu item terakhir. Item belum tentu pernah disimpan, sehingga field dan return value harus diberi tanda mungkin-kosong:
public class LayananCache<T> where T : class
{
private T? itemTerakhir;
public void Simpan(T item)
{
itemTerakhir = item;
}
public T? Ambil()
{
return itemTerakhir;
}
}
LayananCache<string> cacheNama = new LayananCache<string>();
string? hasil = cacheNama.Ambil();
Console.WriteLine(hasil is null ? "Cache masih kosong" : $"Cache berisi {hasil}");
cacheNama.Simpan("Sesi Budi");
string? hasilKedua = cacheNama.Ambil();
Console.WriteLine(hasilKedua is null ? "Cache masih kosong" : $"Cache berisi {hasilKedua}");
int? stokKopi = 40;
int? stokTeh = null;
Console.WriteLine(stokKopi.HasValue ? $"Stok kopi: {stokKopi.Value}" : "Stok kopi kosong");
Console.WriteLine(stokTeh.HasValue ? $"Stok teh: {stokTeh.Value}" : "Stok teh kosong");Output yang Dihasilkan:
Cache masih kosong
Cache berisi Sesi Budi
Stok kopi: 40
Stok teh kosongMari kita bedah baris demi baris:
where T : class+private T? itemTerakhir;: Kombinasi ini menyatakan field boleh berisi null sebelumSimpandipanggil pertama kali.public T? Ambil(): Return type mengumumkan hasil mungkin kosong, sehingga pemanggil diingatkan compiler untuk memeriksanya.hasil is null ? ... : ...: Polais nullmemeriksa kekosongan sebelum nilai dipakai; compiler juga menerima jaminan tipe pada cabang yang benar.int? stokKopi = 40;: Di luar kode generik,int?tetap berartiNullable<int>; propertyHasValuedanValuemilik struct pembungkus itu.stokTeh.HasValue:stokTehberisi null sehinggaHasValuebernilai false dan pesan kosong tercetak.
Constraint Pendukung Nullability #
Constraint where T : notnull menyatakan arah sebaliknya: type argument dijamin bukan null. Constraint ini dipakai Dictionary<TKey, TValue> untuk kunci, dan kita telah melihatnya pada class Kamus<TKey, TValue> di bab Type Parameter.
Dengan tiga makna T? yang terikat constraint, API generik dapat mengumumkan keboleh-kosongan secara presisi kepada seluruh pemanggilnya.