Sejauh ini kita telah menguasai generic class, method, interface, struct, constraints, variance, nullability, dan generic math. Bab penutup section ini menyatukan semuanya menjadi panduan praktis: kapan memilih generics, bagaimana membentuk signature yang ramah pemanggil, dan daftar periksa yang bisa kita pegang saat merancang API sendiri. Titik awalnya adalah membandingkan API berbasis object dengan versi generiknya.

Dari object ke Generik #

Dua class laci berikut menyimpan satu nilai. Versi pertama menerima object; versi kedua memakai type parameter:

csharp
public class LaciObject
{
    private object isi = 0;

    public void Simpan(object data)
    {
        isi = data;
    }

    public object Ambil()
    {
        return isi;
    }
}

public class Laci<T>
{
    private T? isi;

    public void Simpan(T data)
    {
        isi = data;
    }

    public T? Ambil()
    {
        return isi;
    }
}

LaciObject laciLama = new LaciObject();
laciLama.Simpan(45000);
int nilaiLama = (int)laciLama.Ambil();
Console.WriteLine($"Versi object: {nilaiLama}");

Laci<int> laciBaru = new Laci<int>();
laciBaru.Simpan(45000);
Console.WriteLine($"Versi generik: {laciBaru.Ambil()}");

Output yang Dihasilkan:

text
Versi object: 45000
Versi generik: 45000

Mari kita bedah baris demi baris:

  1. laciLama.Simpan(45000): int dibungkus menjadi object (boxing); tipe salah apa pun juga diterima compiler karena semua tipe adalah object.
  2. (int)laciLama.Ambil(): Pemanggil wajib cast, dan kesalahan cast baru terasa saat program berjalan dalam bentuk InvalidCastException.
  3. private T? isi;: Tanpa constraint, T? menyatakan nilai mungkin berupa default(T); maknanya dibedah pada bab Generic Nullability.
  4. laciBaru.Simpan(45000) dan laciBaru.Ambil(): Type argument int mengunci seluruh kontrak; tidak ada boxing, tidak ada cast, dan tipe lain ditolak compiler.

Daftar Periksa Desain API Generik #

  1. Pilih generics daripada object ketika tipe data mengalir dari input ke output; keamanan tipe pindah ke saat kompilasi.
  2. Tambahkan constraint seminimal mungkin yang masih menuntaskan kebutuhan badan kode, sehingga pemanggil tidak dipersulit (bab Generic Constraints).
  3. Buat type inference bekerja dengan memunculkan T pada parameter method, bukan hanya pada return type (bab Generic Type Inference).
  4. Sertakan variance pada kontrak baca dan tulis, out untuk sisi keluaran dan in untuk sisi masukan, agar interface generik menyatu dengan hierarki tipe (bab Covariance dan Contravariance Generics).
  5. Tandai nullability dengan constraint yang tepat, misalnya where T : class dengan T? atau where T : notnull untuk kunci kamus (bab Generic Nullability).
  6. Andalkan INumber untuk API numerik alih-alih overload per tipe angka (bab Generic Math).
  7. Turunkan kontrak baca ke IEnumerable ketika pemanggil hanya butuh membaca urutan data, bukan tipe koleksi konkret (bab Generic Collections).

Dengan daftar periksa di atas, API generik yang kita rancang memberi kepastian tipe penuh pada saat kompilasi sekaligus tetap ringkas bagi pemanggilnya.