Bagikan melalui


Polimorfisme

Polimorfisme sering disebut sebagai pilar ketiga dari pemrograman berorientasi objek, setelah enkapsulasi dan pewarisan. Polimorfisme adalah kata Yunani yang berarti "berbentuk banyak" dan memiliki dua aspek yang berbeda:

  • Pada waktu berjalan, objek dari kelas turunan dapat diperlakukan sebagai objek dari kelas dasar di tempat-tempat seperti parameter metode dan koleksi atau array. Ketika polimorfisme ini terjadi, jenis objek yang dinyatakan tidak lagi identik dengan jenis run-time-nya.
  • Kelas dasar dapat mendefinisikan dan mengimplementasikan metodevirtual, dan kelas turunan dapat menggantikannya, yang berarti mereka memberikan definisi dan implementasi mereka sendiri. Pada run-time, ketika kode klien memanggil metode, CLR mencari jenis run-time dari objek, dan memanggil yang menimpa metode virtual. Dalam kode sumber Anda, Anda dapat memanggil metode pada kelas dasar, dan menyebabkan versi kelas turunan dari metode yang akan dieksekusi.

Metode virtual memungkinkan Anda untuk bekerja dengan kelompok objek terkait dengan cara yang seragam. Misalnya, Anda memiliki aplikasi menggambar yang memungkinkan pengguna untuk membuat berbagai jenis bentuk pada permukaan gambar. Anda tidak tahu pada waktu kompilasi jenis bentuk tertentu yang akan dibuat pengguna. Namun, aplikasi harus melacak semua jenis bentuk yang dibuat, dan harus memperbaruinya sebagai respons terhadap tindakan mouse pengguna. Anda dapat menggunakan polimorfisme untuk memecahkan masalah ini dalam dua langkah dasar:

  1. Buat hierarki kelas di mana setiap kelas bentuk tertentu berasal dari kelas dasar umum.
  2. Gunakan metode virtual untuk memanggil metode yang sesuai pada setiap kelas turunan melalui satu panggilan ke metode kelas dasar.

Pertama, buat kelas dasar yang disebut Shape, dan turunan kelas seperti Rectangle, Circle, dan Triangle. Shape Berikan kelas metode virtual yang disebut Draw, dan ambil alih di setiap kelas turunan untuk menggambar bentuk tertentu yang diwakili kelas. Buat List<Shape> objek dan tambahkan Circle, Triangle, dan Rectangle ke dalamnya.

public class Shape
{
    // A few example members
    public int X { get; private set; }
    public int Y { get; private set; }
    public int Height { get; set; }
    public int Width { get; set; }

    // Virtual method
    public virtual void Draw()
    {
        Console.WriteLine("Performing base class drawing tasks");
    }
}

public class Circle : Shape
{
    public override void Draw()
    {
        // Code to draw a circle...
        Console.WriteLine("Drawing a circle");
        base.Draw();
    }
}
public class Rectangle : Shape
{
    public override void Draw()
    {
        // Code to draw a rectangle...
        Console.WriteLine("Drawing a rectangle");
        base.Draw();
    }
}
public class Triangle : Shape
{
    public override void Draw()
    {
        // Code to draw a triangle...
        Console.WriteLine("Drawing a triangle");
        base.Draw();
    }
}

Untuk memperbarui permukaan gambar, gunakan perulangan foreach untuk melakukan iterasi melalui daftar dan memanggil Draw metode pada setiap Shape objek dalam daftar. Meskipun setiap objek dalam daftar memiliki jenis yang dinyatakan Shape, itu adalah jenis run-time (versi metode yang ditimpa di setiap kelas turunan) yang akan dipanggil.

// Polymorphism at work #1: a Rectangle, Triangle and Circle
// can all be used wherever a Shape is expected. No cast is
// required because an implicit conversion exists from a derived
// class to its base class.
var shapes = new List<Shape>
{
    new Rectangle(),
    new Triangle(),
    new Circle()
};

// Polymorphism at work #2: the virtual method Draw is
// invoked on each of the derived classes, not the base class.
foreach (var shape in shapes)
{
    shape.Draw();
}
/* Output:
    Drawing a rectangle
    Performing base class drawing tasks
    Drawing a triangle
    Performing base class drawing tasks
    Drawing a circle
    Performing base class drawing tasks
*/

Dalam C#, setiap jenis adalah polimorfik karena semua jenis, termasuk jenis yang ditentukan pengguna, diwarisi dari Object.

Gambaran umum polimorfisme

Anggota virtual

Ketika kelas turunan mewarisi dari kelas dasar, kelas ini mencakup semua anggota kelas dasar. Semua perilaku yang dideklarasikan dalam kelas dasar adalah bagian dari kelas turunan. Itu memungkinkan objek dari kelas turunan diperlakukan sebagai objek kelas dasar. Pengubah akses (public, protected, private dan sebagainya) menentukan apakah anggota tersebut dapat diakses dari implementasi kelas turunan. Metode virtual memberi perancang pilihan yang berbeda untuk perilaku kelas turunan:

  • Kelas turunan dapat menimpa anggota virtual di kelas dasar, mendefinisikan perilaku baru.
  • Kelas turunan dapat mewarisi metode kelas dasar terdekat tanpa mengesampingkannya, melestarikan perilaku yang ada tetapi memungkinkan kelas turunan lebih lanjut untuk mengesampingkan metode tersebut.
  • Kelas turunan dapat menentukan implementasi non-virtual baru dari anggota yang menyembunyikan implementasi kelas dasar.

Kelas turunan dapat mengambil alih anggota kelas dasar hanya jika anggota kelas dasar dinyatakan sebagai virtual atau abstrak. Anggota turunan harus menggunakan kata kunci ambil alih untuk secara eksplisit menunjukkan bahwa metode dimaksudkan untuk berpartisipasi dalam pemanggilan virtual. Kode berikut memberikan contoh:

public class BaseClass
{
    public virtual void DoWork() { }
    public virtual int WorkProperty
    {
        get { return 0; }
    }
}
public class DerivedClass : BaseClass
{
    public override void DoWork() { }
    public override int WorkProperty
    {
        get { return 0; }
    }
}

Bidang tidak bisa virtual; hanya metode, properti, acara, dan pengindeks yang dapat menjadi virtual. Ketika kelas turunan menimpa anggota virtual, anggota tersebut dipanggil bahkan ketika instance dari kelas tersebut diakses sebagai instance dari kelas dasar. Kode berikut memberikan contoh:

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = B;
A.DoWork();  // Also calls the new method.

Metode dan properti virtual memungkinkan kelas turunan untuk memperluas kelas dasar tanpa perlu menggunakan implementasi kelas dasar suatu metode. Untuk informasi selengkapnya, lihat Penerapan versi dengan Penimpaan dan Kata Kunci Baru. Antarmuka menyediakan cara lain untuk menentukan metode atau serangkaian metode yang implementasinya diserahkan kepada kelas turunan.

Menyembunyikan anggota kelas dasar dengan anggota baru

Jika Anda ingin kelas turunan Anda memiliki anggota dengan nama yang sama dengan anggota di kelas dasar, Anda dapat menggunakan kata kunci baru untuk menyembunyikan anggota kelas dasar. Kata new kunci dimasukkan sebelum jenis pengembalian anggota kelas yang sedang diganti. Kode berikut memberikan contoh:

public class BaseClass
{
    public void DoWork() { WorkField++; }
    public int WorkField;
    public int WorkProperty
    {
        get { return 0; }
    }
}

public class DerivedClass : BaseClass
{
    public new void DoWork() { WorkField++; }
    public new int WorkField;
    public new int WorkProperty
    {
        get { return 0; }
    }
}

Anggota kelas dasar tersembunyi dapat diakses dari kode klien dengan memberikan instance kelas turunan ke instance kelas dasar. Misalnya:

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Calls the old method.

Mencegah kelas turunan dari mengesampingkan anggota virtual

Anggota virtual tetap virtual, terlepas dari berapa banyak kelas yang telah dinyatakan antara anggota virtual dan kelas yang awalnya menyatakannya. Jika kelas A menyatakan anggota virtual, dan kelas B berasal dari A, dan kelas C berasal dari B, kelas C mewarisi anggota virtual, dan dapat mengambil alihnya, terlepas dari apakah kelas B menyatakan penimpaan untuk anggota tersebut. Kode berikut memberikan contoh:

public class A
{
    public virtual void DoWork() { }
}
public class B : A
{
    public override void DoWork() { }
}

Kelas turunan dapat menghentikan warisan virtual dengan menyatakan penimpaan sebagai disegel. Menghentikan pewarisan mengharuskan menempatkan sealed kata kunci sebelum override kata kunci dalam deklarasi anggota kelas. Kode berikut memberikan contoh:

public class C : B
{
    public sealed override void DoWork() { }
}

Dalam contoh sebelumnya, metode DoWork ini tidak lagi virtual untuk kelas apa pun yang berasal dari C. Ini masih virtual untuk instans C, bahkan jika mereka dilemparkan ke jenis B atau jenis A. Metode yang disegel dapat digantikan oleh kelas turunan dengan menggunakan kata kunci new, seperti yang ditunjukkan contoh berikut:

public class D : C
{
    public new void DoWork() { }
}

Dalam hal ini, jika DoWork dipanggil menggunakan D variabel jenis D, yang baru DoWork dipanggil. Jika variabel jenis C, B, atau A digunakan untuk mengakses instans D, panggilan ke DoWork akan mengikuti aturan pewarisan virtual, merutekan panggilan tersebut ke implementasi DoWork di kelas C.

Akses anggota virtual kelas dasar dari kelas turunan

Kelas turunan yang telah menggantikan atau menimpa metode atau properti masih dapat mengakses metode atau properti pada kelas dasar menggunakan kata kunci base. Kode berikut memberikan contoh:

public class Base
{
    public virtual void DoWork() {/*...*/ }
}
public class Derived : Base
{
    public override void DoWork()
    {
        //Perform Derived's work here
        //...
        // Call DoWork on base class
        base.DoWork();
    }
}

Untuk informasi selengkapnya, lihat dasar.

Catatan

Disarankan agar anggota virtual menggunakan base untuk memanggil implementasi kelas dasar anggota tersebut dalam implementasi mereka sendiri. Membiarkan perilaku kelas dasar terjadi memungkinkan kelas turunan untuk berkonsentrasi pada penerapan perilaku khusus untuk kelas turunan. Jika implementasi kelas dasar tidak dipanggil, terserah kelas turunan untuk membuat perilaku mereka kompatibel dengan perilaku kelas dasar.