Pada kode non-generik, int? punya satu makna: pembungkus Nullable<int>. Pada kode generik, compiler belum tahu apakah T adalah tipe nilai atau tipe referensi, sehingga makna T? diikat pada constraint yang menyertainya. Bab Generic Nullability di section Generics sudah memperkenalkan peta maknanya; bab ini menekankan mekanisme runtime dan method generik yang mengembalikan hasil mungkin-kosong.
Makna T? yang Terikat Constraint #
| Constraint | Makna T? |
|---|---|
where T : struct | Singkatan Nullable<T>, pembungkus struct sungguhan |
where T : class | Nullable reference type, murni anotasi untuk compiler |
| Tanpa constraint | T boleh berisi default(T) sejak C# 9 |
Ketiga bentuk mengumumkan hal yang sama, yaitu hasil bisa kosong, tetapi mekanismenya berbeda: satu bentuk menambah struct pembungkus, dua bentuk lainnya hanya anotasi.
Contoh: Method Generik dengan Hasil Mungkin Kosong #
T? CariPertama<T>(IEnumerable<T> sumber, Func<T, bool> syarat) where T : class
{
foreach (T item in sumber)
{
if (syarat(item))
{
return item;
}
}
return null;
}
List<string> produk = new List<string> { "Kopi Gayo", "Teh Melati", "Cokelat Lampung" };
string? hasilTeh = CariPertama(produk, p => p.StartsWith("Teh"));
string? hasilGula = CariPertama(produk, p => p.StartsWith("Gula"));
Console.WriteLine(hasilTeh ?? "Tidak ditemukan");
Console.WriteLine(hasilGula ?? "Tidak ditemukan");
Console.WriteLine($"int? sama dengan Nullable<int>: {typeof(int?) == typeof(Nullable<int>)}");
Console.WriteLine($"Underlying type: {Nullable.GetUnderlyingType(typeof(int?))!.Name}");
Console.WriteLine($"Nullable<double> adalah struct: {typeof(Nullable<double>).IsValueType}");Output yang Dihasilkan:
Teh Melati
Tidak ditemukan
int? sama dengan Nullable<int>: True
Underlying type: Int32
Nullable<double> adalah struct: TrueMari kita bedah baris demi baris:
T? CariPertama<T>(...) where T : class: ConstraintclassmembuatT?berarti nullable reference type, sehinggareturn nullsah tanpa warning.foreach (T item in sumber): Pencarian berhenti pada item pertama yang lolos syarat; bila tidak ada, method jatuh kereturn null.string? hasilTeh: Type inference menetapkanTsebagaistring, dan hasil method diwarisi sebagaistring?.typeof(int?) == typeof(Nullable<int>): Perbandingan tipe membuktikan bahwaint?bukan tipe baru, melainkan nama lainNullable<int>.Nullable.GetUnderlyingType(...): Method utilitas mengembalikanInt32, yaitu tipe nilai yang dibungkus.IsValueTypepadaNullable<double>: HasilTruemenegaskan pembungkus ini struct, sehingga ia disalin per nilai dan hidup di stack seperti value type lain.
Boxing Pembungkus Generik #
Perilaku boxing pada bab Nullable Value Types berlaku sama untuk semua Nullable<T>: pembungkus berisi nilai di-boxing sebagai T murni, dan pembungkus kosong di-boxing sebagai null reference. Karena itu API yang menerima object tidak bisa membedakan int biasa dengan int? yang terisi.