Generik di .NET

Generik memungkinkan Anda menyesuaikan metode, kelas, struktur, atau antarmuka hingga jenis data spesifik yang ditindaklanjuti. Misalnya, alih-alih menggunakan kelas Hashtable, yang memungkinkan kunci dan nilai untuk menjadi jenis apa pun, Anda dapat menggunakan kelas generik Dictionary<TKey,TValue> dan menentukan jenis yang diizinkan untuk kunci dan nilai. Di antara keuntungan generik adalah peningkatan kemampuan penggunaan kembali kode dan keamanan jenis.

Mendefinisikan dan Menggunakan Generik

Generik adalah kelas, struktur, antarmuka, dan metode yang memiliki tempat penampung (parameter jenis) untuk satu atau beberapa jenis yang disimpan atau digunakan. Kelas koleksi generik mungkin menggunakan parameter tipe sebagai tempat penampung untuk jenis objek yang disimpannya; parameter tipe muncul sebagai jenis dari bidangnya dan jenis parameter dari metodenya. Metode generik mungkin menggunakan parameter jenisnya sebagai jenis nilai yang dikembalikan atau sebagai jenis dari salah satu parameter formalnya. Kode berikut menggambarkan definisi kelas generik sederhana.

generic<typename T>
public ref class Generics
{
public:
    T Field;
};
public class Generic<T>
{
    public T Field;
}
Public Class Generic(Of T)
    Public Field As T

End Class

Saat Anda membuat instans kelas generik, Anda menentukan jenis sebenarnya untuk menggantikan parameter jenis. Ini menetapkan kelas generik baru, yang disebut sebagai kelas generik yang dibangun, sehingga jenis yang Anda pilih digantikan di mana pun parameter jenis muncul. Hasilnya adalah kelas jenis aman yang disesuaikan dengan pilihan jenis Anda, seperti yang digambarkan oleh kode berikut.

static void Main()
{
    Generics<String^>^ g = gcnew Generics<String^>();
    g->Field = "A string";
    //...
    Console::WriteLine("Generics.Field           = \"{0}\"", g->Field);
    Console::WriteLine("Generics.Field.GetType() = {0}", g->Field->GetType()->FullName);
}
public static void Main()
{
    Generic<string> g = new Generic<string>();
    g.Field = "A string";
    //...
    Console.WriteLine("Generic.Field           = \"{0}\"", g.Field);
    Console.WriteLine("Generic.Field.GetType() = {0}", g.Field.GetType().FullName);
}
Public Shared Sub Main()
    Dim g As New Generic(Of String)
    g.Field = "A string"
    '...
    Console.WriteLine("Generic.Field           = ""{0}""", g.Field)
    Console.WriteLine("Generic.Field.GetType() = {0}", g.Field.GetType().FullName)
End Sub

Terminologi generik

Istilah berikut digunakan untuk membahas generik di .NET:

  • Definisi jenis generik adalah deklarasi antarmuka, struktur, atau kelas yang berfungsi sebagai templat, dengan tempat penampung untuk jenis yang dapat dimuat atau digunakan. Misalnya, kelas System.Collections.Generic.Dictionary<TKey,TValue> dapat berisi dua jenis: kunci dan nilai. Karena definisi jenis generik hanyalah templat, Anda tidak dapat membuat instans kelas, struktur, atau antarmuka yang merupakan definisi jenis generik.

  • Parameter jenis generik, atau parameter jenis, adalah tempat penampung dalam definisi jenis generik atau metode. Jenis generik System.Collections.Generic.Dictionary<TKey,TValue> memiliki dua parameter jenis, TKey dan TValue, yang mewakili jenis dari kunci dan nilainya.

  • Jenis generik yang dibangun, atau jenis yang dibangun, adalah hasil dari menentukan jenis untuk parameter jenis generik dari definisi jenis generik.

  • Argumen jenis generik adalah jenis apa pun yang digantikan untuk parameter jenis generik.

  • Istilah umum jenis generik mencakup definisi jenis generik dan jenis yang dibangun.

  • Kovarian dan kontravarian parameter jenis generik memungkinkan Anda menggunakan jenis generik yang dibangun yang argumen jenisnya lebih diturunkan (kovarian) atau kurang diturunkan (kontravarian) daripada jenis yang dibangun target. Kovarian dan kontravarian secara kolektif disebut sebagai variansi. Untuk mengetahui informasi selengkapnya, lihat Kovarian and Kontravarian.

  • Batasan adalah batas yang ditempatkan pada parameter jenis generik. Misalnya, Anda dapat membatasi parameter jenis untuk jenis yang mengimplementasikan antarmuka generik System.Collections.Generic.IComparer<T>, untuk memastikan bahwa instans jenis dapat diurutkan. Anda juga dapat membatasi parameter jenis ke jenis yang memiliki kelas dasar tertentu, yang memiliki konstruktor tanpa parameter, atau yang merupakan jenis referensi atau jenis nilai. Pengguna dari jenis generik tidak dapat mengganti argumen jenis yang tidak mematuhi batasan.

  • Definisi metode generik adalah metode dengan dua daftar parameter: daftar parameter jenis generik dan daftar parameter formal. Parameter jenis dapat muncul sebagai jenis pengembalian atau sebagai jenis parameter formal, seperti yang ditunjukkan kode berikut.

generic<typename T>
T Generic(T arg)
{
    T temp = arg;
    //...
    return temp;
}
T Generic<T>(T arg)
{
    T temp = arg;
    //...
    return temp;
}
Function Generic(Of T)(ByVal arg As T) As T
    Dim temp As T = arg
    '...
    Return temp
End Function

Metode generik dapat muncul pada jenis generik atau nongenerik. Penting untuk diperhatikan bahwa suatu metode tidak lantas menjadi generik hanya karena termasuk dalam jenis generik, atau bahkan karena memiliki parameter formal yang jenisnya adalah parameter generik dari tipe penutup. Sebuah metode dianggap generik hanya jika memiliki daftar parameter jenisnya sendiri. Dalam kode berikut, hanya metode G yang generik.

ref class A
{
    generic<typename T>
    T G(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
};
generic<typename T>
ref class Generic
{
    T M(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
};
class A
{
    T G<T>(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
}
class Generic<T>
{
    T M(T arg)
    {
        T temp = arg;
        //...
        return temp;
    }
}
Class A
    Function G(Of T)(ByVal arg As T) As T
        Dim temp As T = arg
        '...
        Return temp
    End Function
End Class
Class Generic(Of T)
    Function M(ByVal arg As T) As T
        Dim temp As T = arg
        '...
        Return temp
    End Function
End Class

Kelebihan dan kekurangan generik

Ada banyak keuntungan menggunakan koleksi dan delegasi generik:

  • Keselamatan jenis. Generik mengalihkan beban keamanan jenis dari Anda ke pengompilasi. Tidak perlu menulis kode untuk menguji jenis data yang benar karena itu diberlakukan pada saat kompilasi. Kebutuhan untuk mentransmisikan jenis dan kemungkinan kesalahan run-time berkurang.

  • Kode lebih sedikit dan lebih bisa digunakan kembali. Tidak perlu mewarisi dari jenis dasar dan mengambil alih anggota. Misalnya, LinkedList<T> siap segera digunakan. Misalnya, Anda dapat membuat daftar string tertaut dengan deklarasi variabel berikut:

    LinkedList<String^>^ llist = gcnew LinkedList<String^>();
    
    LinkedList<string> llist = new LinkedList<string>();
    
    Dim llist As New LinkedList(Of String)()
    
  • Performa yang lebih baik. Jenis koleksi generik umumnya memiliki performa lebih baik untuk menyimpan dan memanipulasi jenis nilai karena tidak perlu menyimpan jenis nilai.

  • Delegasi generik mengaktifkan panggilan balik jenis aman tanpa perlu membuat beberapa kelas delegasi. Misalnya, delegasi generik Predicate<T> memungkinkan Anda membuat metode yang mengimplementasikan kriteria pencarian Anda sendiri untuk jenis tertentu dan menggunakan metode Anda dengan metode jenis Array seperti Find, FindLast, dan FindAll.

  • Generik merampingkan kode yang dihasilkan secara dinamis. Saat Anda menggunakan generik dengan kode yang dihasilkan secara dinamis, Anda tidak perlu menghasilkan jenisnya. Hal ini meningkatkan jumlah skenario saat Anda dapat menggunakan metode dinamis ringan alih-alih menghasilkan seluruh rakitan. Untuk mengetahui informasi selengkapnya, lihat Petunjuk: Menentukan dan Menjalankan Metode Dinamis dan DynamicMethod.

Berikut ini adalah beberapa batasan generik:

  • Jenis generik dapat berasal dari sebagian besar kelas dasar, seperti MarshalByRefObject (dan batasan dapat digunakan untuk mengharuskan parameter jenis generik berasal dari kelas dasar seperti MarshalByRefObject). Namun, .NET tidak mendukung jenis generik yang terikat konteks. Jenis generik dapat diturunkan dari ContextBoundObject, tetapi mencoba membuat instans jenis tersebut menyebabkan TypeLoadException.

  • Enumerasi tidak dapat memiliki parameter jenis generik. Enumerasi hanya dapat bersifat generik secara tidak sengaja (misalnya, karena bersarang dalam jenis generik yang didefinisikan menggunakan Visual Basic, C#, atau C ++). Untuk mengetahui informasi selengkapnya, lihat "Enumerasi" di Sistem Jenis Umum.

  • Metode dinamis ringan tidak boleh generik.

  • Dalam Visual Basic, C#, dan C ++, jenis berlapis yang terlampir dalam jenis generik tidak dapat dibuat instans-nya kecuali jenis telah ditetapkan ke parameter jenis dari semua jenis penutup. Cara lain untuk menjelaskan ini adalah bahwa dalam refleksi, jenis berlapis yang didefinisikan menggunakan bahasa-bahasa ini mencakup parameter jenis dari semua jenis penutupnya. Hal ini memungkinkan parameter jenis dari jenis penutup untuk digunakan dalam definisi anggota dari jenis berlapis. Untuk mengetahui informasi selengkapnya, lihat "Jenis Berlapis" di MakeGenericType.

    Catatan

    Jenis berlapis yang didefinisikan dengan memancarkan kode dalam rakitan dinamis atau dengan menggunakan Ilasm.exe (IL Assembler) tidak diharuskan untuk menyertakan parameter jenis dari jenis penutupnya; namun, jika tidak menyertakannya, parameter jenis tidak berada dalam cakupan di kelas berlapis.

    Untuk mengetahui informasi selengkapnya, lihat "Jenis Berlapis" di MakeGenericType.

Pustaka Kelas .NET Framework dan Dukungan Bahasa Pemrogram

.NET menyediakan sejumlah kelas koleksi generik di namespace layanan berikut:

Antarmuka generik untuk menerapkan perbandingan pengurutan dan kesetaraan disediakan di namespace layanan System, bersama dengan jenis delegasi generik untuk penanganan aktivitas, konversi, dan predikat pencarian.

Dukungan untuk generik telah ditambahkan ke namespace layanan System.Reflection untuk memeriksa jenis generik dan metode generik, ke System.Reflection.Emit untuk memancarkan rakitan dinamis yang berisi metode dan jenis generik, dan ke System.CodeDom untuk menghasilkan grafik sumber yang menyertakan generik.

Runtime bahasa umum menyediakan opcode dan prefiks untuk mendukung jenis generik dalam bahasa perantara Microsoft (MSIL), termasuk Stelem, Ldelem, Unbox_Any, Constrained, dan Readonly.

Visual C++, C#, dan Visual Basic semuanya memberikan dukungan penuh untuk mendefinisikan dan menggunakan generik. Untuk mengetahui informasi selengkapnya tentang dukungan bahasa pemrogram, lihat Jenis Generik di Visual Basic, Pengantar Generik, dan Gambaran Umum Generik di Visual C++.

Jenis Berlapis dan Generik

Jenis yang berlapis dalam jenis generik dapat bergantung pada parameter jenis dari jenis generik penutup. Runtime bahasa umum menganggap jenis berlapis sebagai generik, bahkan jika mereka tidak memiliki parameter jenis generik mereka sendiri. Saat Anda membuat instans tipe berlapis, Anda harus menentukan argumen jenis untuk semua jenis generik penutup.

Judul Deskripsi
Koleksi Generik di .NET Menjelaskan kelas koleksi generik dan jenis generik lainnya di .NET.
Delegasi Generik untuk Memanipulasi Larik dan Daftar Menjelaskan delegasi generik untuk konversi, predikat pencarian, dan tindakan yang akan diambil pada elemen larik atau koleksi.
Antarmuka Generik Menjelaskan antarmuka generik yang menyediakan fungsionalitas umum di seluruh keluarga jenis generik.
Kovarian dan Kontravarian Menjelaskan kovarian dan kontravarian dalam parameter jenis generik.
Jenis Koleksi yang Umum Digunakan Memberikan informasi ringkasan tentang skenario penggunaan dan karakteristik jenis koleksi di .NET, termasuk jenis generik.
Kapan Koleksi Generik Digunakan Menjelaskan aturan umum untuk menentukan kapan jenis koleksi generik harus digunakan.
Petunjuk: Menentukan Jenis Generik dengan Pancaran Refleksi Menjelaskan cara menghasilkan rakitan dinamis yang mencakup metode dan jenis generik.
Jenis Generik di Visual Basic Menjelaskan fitur generik untuk pengguna Visual Basic, termasuk topik petunjuk menggunakan dan menentukan jenis generik.
Pengantar Generik Memberikan gambaran umum tentang mendefinisikan dan menggunakan jenis generik untuk pengguna C#.
Gambaran umum Generik di Visual C++ Menjelaskan fitur generik untuk pengguna C ++, termasuk perbedaan antara generik dan templat.

Referensi

System.Collections.Generic

System.Collections.ObjectModel

System.Reflection.Emit.OpCodes