Pewarisan - mendapatkan jenis untuk membuat perilaku yang lebih khusus

Warisan, bersama dengan enkapsulasi dan polimorfisme, adalah salah satu dari tiga karakteristik utama pemrograman berorientasi objek. Pewarisan memungkinkan Anda membuat kelas baru yang menggunakan kembali, memperluas, dan memodifikasi perilaku yang ditentukan di kelas lain. Kelas yang anggotanya diwariskan disebut kelas dasar, dan kelas yang mewarisi anggota tersebut disebut kelas turunan. Kelas turunan hanya dapat memiliki satu kelas dasar langsung. Namun, warisan bersifat transitif. Jika ClassC berasal dari ClassB, dan ClassB berasal dari ClassA, ClassC mewarisi anggota yang dinyatakan dalam ClassB dan ClassA.

Catatan

Struct tidak mendukung warisan, tetapi dapat mengimplementasikan antarmuka.

Secara konseptual, kelas turunan adalah spesialisasi kelas dasar. Misalnya, jika Anda memiliki kelas dasar Animal, Anda mungkin memiliki satu kelas turunan yang diberi nama Mammal dan kelas turunan lain yang diberi nama Reptile. Mammal adalah Animal, dan Reptile adalah Animal, tetapi setiap kelas turunan mewakili spesialisasi yang berbeda dari kelas dasar.

Deklarasi antarmuka dapat menentukan implementasi default untuk anggotanya. Implementasi ini diwarisi oleh antarmuka turunan, dan oleh kelas yang mengimplementasikan antarmuka tersebut. Untuk informasi selengkapnya tentang metode antarmuka default, lihat artikel tentang antarmuka.

Ketika Anda mendefinisikan kelas yang berasal dari kelas lain, kelas turunan secara implisit mendapatkan semua anggota kelas dasar, kecuali konstruktor dan penyelesainya. Kelas turunan menggunakan kembali kode di kelas dasar tanpa harus menerapkannya kembali. Anda dapat menambahkan lebih banyak anggota di kelas turunan. Kelas turunan memperluas fungsionalitas kelas dasar.

Ilustrasi berikut menunjukkan kelas WorkItem yang mewakili item pekerjaan dalam beberapa proses bisnis. Seperti semua kelas, ia berasal dari System.Object dan mewarisi semua metodenya. WorkItem menambahkan enam anggotanya sendiri. Anggota ini termasuk konstruktor, karena konstruktor tidak diwariskan. Kelas ChangeRequest mewarisi dari WorkItem dan mewakili jenis item kerja tertentu. ChangeRequest menambahkan dua anggota lagi ke anggota yang diwarisinya dari WorkItem dan dari Object. Ini harus menambahkan konstruktornya sendiri, dan juga menambahkan originalItemID. Properti originalItemID memungkinkan instans ChangeRequest dikaitkan dengan asli WorkItem ke tempat diterapkan permintaan perubahan.

Diagram that shows class inheritance

Contoh berikut menunjukkan bagaimana hubungan kelas yang ditunjukkan dalam ilustrasi sebelumnya dinyatakan dalam C#. Contohnya juga menunjukkan bagaimana WorkItem menimpa metode virtual Object.ToString, dan bagaimana kelas ChangeRequest mewarisi WorkItem implementasi metode. Blok pertama mendefinisikan kelas:

// WorkItem implicitly inherits from the Object class.
public class WorkItem
{
    // Static field currentID stores the job ID of the last WorkItem that
    // has been created.
    private static int currentID;

    //Properties.
    protected int ID { get; set; }
    protected string Title { get; set; }
    protected string Description { get; set; }
    protected TimeSpan jobLength { get; set; }

    // Default constructor. If a derived class does not invoke a base-
    // class constructor explicitly, the default constructor is called
    // implicitly.
    public WorkItem()
    {
        ID = 0;
        Title = "Default title";
        Description = "Default description.";
        jobLength = new TimeSpan();
    }

    // Instance constructor that has three parameters.
    public WorkItem(string title, string desc, TimeSpan joblen)
    {
        this.ID = GetNextID();
        this.Title = title;
        this.Description = desc;
        this.jobLength = joblen;
    }

    // Static constructor to initialize the static member, currentID. This
    // constructor is called one time, automatically, before any instance
    // of WorkItem or ChangeRequest is created, or currentID is referenced.
    static WorkItem() => currentID = 0;

    // currentID is a static field. It is incremented each time a new
    // instance of WorkItem is created.
    protected int GetNextID() => ++currentID;

    // Method Update enables you to update the title and job length of an
    // existing WorkItem object.
    public void Update(string title, TimeSpan joblen)
    {
        this.Title = title;
        this.jobLength = joblen;
    }

    // Virtual method override of the ToString method that is inherited
    // from System.Object.
    public override string ToString() =>
        $"{this.ID} - {this.Title}";
}

// ChangeRequest derives from WorkItem and adds a property (originalItemID)
// and two constructors.
public class ChangeRequest : WorkItem
{
    protected int originalItemID { get; set; }

    // Constructors. Because neither constructor calls a base-class
    // constructor explicitly, the default constructor in the base class
    // is called implicitly. The base class must contain a default
    // constructor.

    // Default constructor for the derived class.
    public ChangeRequest() { }

    // Instance constructor that has four parameters.
    public ChangeRequest(string title, string desc, TimeSpan jobLen,
                         int originalID)
    {
        // The following properties and the GetNexID method are inherited
        // from WorkItem.
        this.ID = GetNextID();
        this.Title = title;
        this.Description = desc;
        this.jobLength = jobLen;

        // Property originalItemID is a member of ChangeRequest, but not
        // of WorkItem.
        this.originalItemID = originalID;
    }
}

Blok berikut ini menunjukkan cara menggunakan kelas dasar dan turunan:

// Create an instance of WorkItem by using the constructor in the
// base class that takes three arguments.
WorkItem item = new WorkItem("Fix Bugs",
                            "Fix all bugs in my code branch",
                            new TimeSpan(3, 4, 0, 0));

// Create an instance of ChangeRequest by using the constructor in
// the derived class that takes four arguments.
ChangeRequest change = new ChangeRequest("Change Base Class Design",
                                        "Add members to the class",
                                        new TimeSpan(4, 0, 0),
                                        1);

// Use the ToString method defined in WorkItem.
Console.WriteLine(item.ToString());

// Use the inherited Update method to change the title of the
// ChangeRequest object.
change.Update("Change the Design of the Base Class",
    new TimeSpan(4, 0, 0));

// ChangeRequest inherits WorkItem's override of ToString.
Console.WriteLine(change.ToString());
/* Output:
    1 - Fix Bugs
    2 - Change the Design of the Base Class
*/

Metode abstrak dan virtual

Ketika kelas dasar mendeklarasikan metode sebagai virtual, kelas turunan dapat override menggunakan metode dengan implementasinya sendiri. Jika kelas dasar menyatakan anggota sebagai abstract, metode tersebut harus ditimpa di kelas non-abstrak yang langsung mewarisi dari kelas tersebut. Jika kelas turunan itu sendiri abstrak, ia mewarisi anggota abstrak tanpa menerapkannya. Anggota abstrak dan virtual adalah dasar untuk polimorfisme, yang merupakan karakteristik utama kedua dari pemrograman berorientasi objek. Untuk informasi selengkapnya, lihat Polymorphism.

Kelas dasar abstrak

Anda dapat mendeklarasikan kelas sebagai abstrak jika Anda ingin mencegah representasi instans langsung dengan menggunakan operator baru. Kelas abstrak hanya dapat digunakan jika kelas baru berasal darinya. Kelas abstrak dapat berisi satu atau lebih tanda tangan metode yang dengan sendirinya dinyatakan sebagai abstrak. Tanda tangan ini menentukan parameter dan nilai pengembalian tetapi tidak memiliki implementasi (badan metode). Kelas abstrak tidak harus berisi anggota abstrak; Namun, jika kelas memang berisi anggota abstrak, kelas itu sendiri harus dinyatakan sebagai abstrak. Kelas turunan yang tidak abstrak sendiri harus menyediakan implementasi untuk metode abstrak dari kelas dasar abstrak.

Antarmuka

Antarmuka adalah jenis referensi yang mendefinisikan sekumpulan anggota. Semua kelas dan struktur yang mengimplementasikan antarmuka itu harus mengimplementasikan sekumpulan anggota tersebut. Antarmuka dapat menentukan implementasi default untuk salah satu atau semua anggota ini. Kelas dapat mengimplementasikan beberapa antarmuka meskipun hanya dapat berasal dari satu kelas dasar langsung.

Antarmuka digunakan untuk menentukan kemampuan khusus untuk kelas yang belum tentu memiliki hubungan "is a". Misalnya, antarmuka System.IEquatable<T> dapat diimplementasikan oleh kelas atau struktur apa pun untuk menentukan apakah dua objek jenis tersebut setara (namun jenisnya mendefinisikan kesetaraan). IEquatable<T> tidak menyiratkan jenis hubungan "is a" yang sama yang ada antara kelas dasar dan kelas turunan (misalnya, Mammaladalah Animal). Untuk informasi lebih lanjut, lihat Antarmuka .

Mencegah derivasi lebih lanjut

Kelas dapat mencegah kelas lain mewarisinya, atau dari salah satu anggotanya, dengan mendeklarasikan dirinya atau anggota sebagai sealed.

Menyembunyikan kelas turunan dari anggota kelas dasar

Kelas turunan dapat menyembunyikan anggota kelas dasar dengan mendeklarasikan anggota dengan nama dan tanda tangan yang sama. Pengubah new dapat digunakan untuk secara eksplisit menunjukkan bahwa anggota tidak dimaksudkan untuk menjadi penimpaan anggota dasar. Penggunaan new tidak diperlukan, tetapi peringatan kompiler akan dihasilkan jika new tidak digunakan. Untuk informasi selengkapnya, lihat Membuat Versi dengan Penimpaan dan Kata Kunci Baru dan Mengetahui Kapan Harus Menggunakan Override dan Kata Kunci Baru.