Kelas Generik (Panduan Pemrograman C#)

Kelas generik merangkum operasi yang tidak spesifik untuk jenis data tertentu. Penggunaan yang paling umum untuk kelas generik adalah dengan koleksi seperti daftar tertaut, tabel hash, tumpukan, antrean, pohon, dan banyak lagi. Operasi seperti menambahkan dan menghapus item dari koleksi dilakukan dengan cara yang sama, terlepas dari jenis data yang disimpan.

Untuk sebagian besar skenario yang memerlukan kelas koleksi, pendekatan yang disarankan adalah menggunakan yang disediakan di pustaka kelas .NET. Untuk informasi selengkapnya tentang menggunakan kelas-kelas ini, lihat Koleksi Generik di .NET.

Biasanya, Anda membuat kelas generik dengan memulai dengan kelas konkret yang ada, dan mengubah jenis menjadi parameter jenis satu per satu hingga mencapai keseimbangan generalisasi dan kegunaan yang optimal. Saat membuat kelas generik Anda sendiri, pertimbangan penting meliputi hal-hal berikut:

  • Jenis mana yang akan digeneralisasi menjadi parameter jenis.

    Sebagai aturan, semakin banyak jenis yang dapat Anda parameterisasi, semakin fleksibel dan dapat digunakan kembali kode Anda. Namun, terlalu banyak generalisasi dapat membuat kode yang sulit dibaca atau dipahami oleh pengembang lain.

  • Batasan apa, jika ada, untuk diterapkan ke parameter jenis (Lihat Batasan pada Parameter Jenis).

    Aturan yang baik adalah menerapkan batasan maksimum yang mungkin masih akan memungkinkan Anda menangani jenis yang harus ditangani. Misalnya, jika tahu bahwa kelas generik Anda dimaksudkan untuk digunakan hanya dengan jenis referensi, terapkan batasan kelas. Batasan itu akan mencegah penggunaan kelas yang tidak diinginkan dengan jenis nilai, dan akan memungkinkan Anda untuk menggunakan operator as pada T, dan memeriksa nilai null.

  • Apakah akan memperhitungkan perilaku generik ke dalam kelas dasar dan subkelas.

    Karena kelas generik dapat berfungsi sebagai kelas dasar, pertimbangan desain yang sama berlaku di sini seperti halnya kelas non-generik. Lihat aturan tentang mewarisi dari kelas dasar generik nanti dalam topik ini.

  • Apakah akan mengimplementasikan satu atau beberapa antarmuka generik.

    Misalnya, jika merancang kelas yang akan digunakan untuk membuat item dalam koleksi berbasis generik, Anda mungkin harus menerapkan antarmuka seperti IComparable<T> di mana T adalah jenis kelas Anda.

Untuk contoh kelas generik sederhana, lihat Pengantar Generik.

Aturan untuk parameter dan batasan jenis memiliki beberapa implikasi untuk perilaku kelas generik, terutama mengenai pewarisan dan aksesibilitas anggota. Sebelum melanjutkan, Anda harus memahami beberapa istilah. Untuk kode klien kelas Node<T>, generik dapat mereferensikan kelas baik dengan menentukan argumen jenis - untuk membuat jenis konstruksi tertutup (Node<int>); atau dengan membiarkan parameter jenis tidak ditentukan - misalnya saat Anda menentukan kelas dasar generik, untuk membuat jenis konstruksi terbuka (Node<T>). Kelas generik dapat mewarisi dari kelas dasar konkret, yang dibangun tertutup, atau yang dibangun terbuka:

class BaseNode { }
class BaseNodeGeneric<T> { }

// concrete type
class NodeConcrete<T> : BaseNode { }

//closed constructed type
class NodeClosed<T> : BaseNodeGeneric<int> { }

//open constructed type
class NodeOpen<T> : BaseNodeGeneric<T> { }

Kelas non-generik, dengan kata lain, konkret dapat mewarisi dari kelas dasar yang dibangun tertutup, tetapi tidak dari kelas yang dibangun terbuka atau dari parameter jenis karena tidak memungkinkan waktu proses bagi kode klien untuk menyediakan argumen jenis yang diperlukan untuk membuat instans kelas dasar.

//No error
class Node1 : BaseNodeGeneric<int> { }

//Generates an error
//class Node2 : BaseNodeGeneric<T> {}

//Generates an error
//class Node3 : T {}

Kelas generik yang mewarisi dari jenis yang dibangun terbuka harus menyediakan argumen jenis untuk parameter jenis kelas dasar apa pun yang tidak dibagikan oleh kelas pewarisan, seperti yang ditunjukkan dalam kode berikut:

class BaseNodeMultiple<T, U> { }

//No error
class Node4<T> : BaseNodeMultiple<T, int> { }

//No error
class Node5<T, U> : BaseNodeMultiple<T, U> { }

//Generates an error
//class Node6<T> : BaseNodeMultiple<T, U> {}

Kelas generik yang mewarisi dari jenis yang dibangun terbuka harus menentukan batasan yang merupakan superset, atau menyiratkan, batasan pada jenis dasar:

class NodeItem<T> where T : System.IComparable<T>, new() { }
class SpecialNodeItem<T> : NodeItem<T> where T : System.IComparable<T>, new() { }

Jenis generik dapat menggunakan beberapa parameter dan batasan jenis, sebagai berikut:

class SuperKeyType<K, V, U>
    where U : System.IComparable<U>
    where V : new()
{ }

Jenis yang dibangun terbuka dan tertutup dapat digunakan sebagai parameter metode:

void Swap<T>(List<T> list1, List<T> list2)
{
    //code to swap items
}

void Swap(List<int> list1, List<int> list2)
{
    //code to swap items
}

Jika kelas generik mengimplementasikan antarmuka, semua instans kelas tersebut dapat ditransmisikan ke antarmuka tersebut.

Kelas generik bersifat invarian. Dengan kata lain, jika parameter input menentukan List<BaseClass>, Anda akan mendapatkan kesalahan waktu kompilasi jika mencoba memberikan List<DerivedClass>.

Lihat juga