Operator armada mengeluarkan laporan deskripsi untuk seluruh kendaraan di garasi. Menulis satu blok if untuk setiap tipe kendaraan membuat kode laporan terus bertambah setiap kali tipe baru hadir. Runtime polymorphism menyelesaikan masalah ini: kode pemanggil cukup memanggil satu method melalui reference bertipe dasar, dan versi implementasi yang benar dipilih runtime sesuai tipe object yang sesungguhnya. Mekanisme ini disebut juga dynamic dispatch, dan output program membuktikan versi mana yang benar-benar berjalan.

Compile-time Type versus Runtime Type #

Satu object membawa dua tipe yang berbeda peran. Compile-time type adalah tipe yang tertulis pada deklarasi variable; tipe ini menentukan member apa yang boleh dipanggil. Runtime type adalah tipe class yang dibuat oleh operator new; tipe inilah yang menentukan versi override mana yang dijalankan. Method GetType() menampilkan runtime type, sehingga kita dapat membuktikan dispatch tersebut pada output.

Contoh: Satu Reference, Dua Tipe #

csharp
public class Kendaraan
{
    public virtual string Deskripsi()
    {
        return "Kendaraan umum";
    }
}

public class Mobil : Kendaraan
{
    public override string Deskripsi()
    {
        return "Mobil pribadi empat roda";
    }
}

public class Motor : Kendaraan
{
    public override string Deskripsi()
    {
        return "Motor roda dua";
    }
}

Kendaraan kendaraanPertama = new Mobil();
Console.WriteLine($"{kendaraanPertama.Deskripsi()} | tipe nyata: {kendaraanPertama.GetType().Name}");

Output yang Dihasilkan:

text
Mobil pribadi empat roda | tipe nyata: Mobil

Mari kita bedah baris demi baris:

  1. public virtual string Deskripsi(): Base class menandai method sebagai titik penggantian, syarat bagi dynamic dispatch.
  2. public override string Deskripsi(): Dua derived class mengisi method yang sama dengan deskripsi berbeda.
  3. Kendaraan kendaraanPertama = new Mobil();: Compile-time type variable adalah Kendaraan, sedangkan runtime type-nya Mobil; dua tipe itu sengaja dibedakan di sini.
  4. kendaraanPertama.Deskripsi(): Runtime memeriksa tipe object yang ditunjuk, lalu menjalankan versi Mobil, bukan versi Kendaraan; output baris pertama menunjukkan hasilnya.
  5. GetType().Name: Runtime type tercetak sebagai Mobil, memperlihatkan bahwa keputusan dispatch mengikuti tipe nyata object, bukan tipe variable.

Dispatch pada Koleksi Campuran #

Polimorfisme baru terasa penuh ketika banyak tipe berdiri dalam satu daftar:

csharp
List<Kendaraan> armada = new List<Kendaraan>
{
    new Mobil(),
    new Motor(),
    new Kendaraan()
};

foreach (Kendaraan kendaraan in armada)
{
    Console.WriteLine($"{kendaraan.Deskripsi()} | tipe nyata: {kendaraan.GetType().Name}");
}

Output yang Dihasilkan:

text
Mobil pribadi empat roda | tipe nyata: Mobil
Motor roda dua | tipe nyata: Motor
Kendaraan umum | tipe nyata: Kendaraan

Perulangan hanya memuat satu baris pemanggilan, tetapi output menampilkan tiga deskripsi berbeda. Runtime memilih versi Mobil untuk elemen pertama, versi Motor untuk elemen kedua, dan versi Kendaraan untuk elemen ketiga; tiga hasil itu terjadi tanpa satu pun pemeriksaan tipe manual di kode kita.

Bab penutup section ini membandingkan inheritance dengan composition, dua cara memakai ulang perilaku yang sering bersaing dalam keputusan desain.