Bagikan melalui


Generik dalam runtime (panduan pemrograman C#)

Ketika jenis atau metode generik dikompilasi ke dalam bahasa perantara umum (CIL), itu berisi metadata yang mengidentifikasinya sebagai memiliki parameter jenis. Perbedaan penggunaan CIL untuk jenis generik berdasarkan apakah parameter jenis yang disediakan adalah jenis nilai atau jenis referensi.

Ketika tipe generik pertama kali dikonstruksi dengan tipe nilai sebagai parameter, runtime membuat tipe generik khusus dengan parameter yang disediakan dimasukkan pada lokasi yang tepat dalam CIL. Jenis generik khusus dibuat satu kali untuk setiap jenis nilai unik yang digunakan sebagai parameter.

Misalnya, kode program Anda mendeklarasikan tumpukan yang dibangun dari bilangan bulat:

Stack<int>? stack;

Pada titik ini, runtime menghasilkan versi khusus kelas Stack<T> yang memiliki bilangan bulat yang diganti dengan tepat untuk parameternya. Sekarang, setiap kali kode program Anda menggunakan tumpukan bilangan bulat, runtime menggunakan kembali kelas khusus Stack<T> yang dihasilkan. Dalam contoh berikut, dua instans tumpukan bilangan bulat dibuat, dan mereka berbagi satu instans Stack<int> kode:

Stack<int> stackOne = new Stack<int>();
Stack<int> stackTwo = new Stack<int>();

Namun, misalkan kelas lain Stack<T> dengan jenis nilai yang berbeda seperti long atau struktur yang ditentukan pengguna karena parameternya dibuat pada titik lain dalam kode Anda. Akibatnya, runtime menghasilkan versi lain dari jenis generik dan menggantikan long di lokasi yang sesuai di CIL. Konversi tidak lagi diperlukan karena setiap kelas generik khusus secara asli berisi jenis nilai.

Generik bekerja agak berbeda untuk jenis referensi. Pertama kali tipe generik dibangun dengan tipe referensi apa pun, runtime membuat tipe generik yang terspesialisasi dengan penggantian referensi objek untuk parameter dalam CIL. Kemudian, setiap kali jenis yang dibentuk diinstansiasi dengan jenis referensi sebagai parameter, terlepas dari jenisnya, runtime menggunakan kembali versi khusus yang dibuat sebelumnya dari jenis generik. Ini dimungkinkan karena semua referensi berukuran sama.

Misalnya, Anda memiliki dua jenis referensi, Customer kelas dan Order kelas, dan juga misalkan Anda membuat tumpukan jenis Customer :

class Customer { }
class Order { }
Stack<Customer> customers;

Pada titik ini, runtime menghasilkan versi khusus dari Stack<T> kelas yang menyimpan referensi objek yang akan diisi nanti alih-alih menyimpan data. Misalkan baris kode berikutnya membuat tumpukan jenis referensi lain, yang diberi nama Order:

Stack<Order> orders = new Stack<Order>();

Berbeda dengan tipe nilai, versi khusus lain dari kelas Stack<T> tidak dibuat untuk tipe Order. Sebagai gantinya, instans versi khusus dari kelas Stack<T> dibuat dan variabel orders disetel untuk mereferensikannya. Misalkan Anda kemudian menemukan baris kode untuk membuat tumpukan jenis Customer :

customers = new Stack<Customer>();

Seperti penggunaan sebelumnya dari kelas Stack<T> yang dibuat dengan menggunakan tipe Order, instans lain dari kelas Stack<T> yang dikhususkan dibuat. Penunjuk yang terdapat di dalamnya diatur untuk mereferensikan area memori dengan ukuran sesuai jenis Customer. Karena jumlah jenis referensi dapat sangat bervariasi dari program ke program, implementasi C# generik sangat mengurangi jumlah kode dengan mengurangi satu jumlah kelas khusus yang dibuat oleh kompilator untuk kelas generik jenis referensi.

Selain itu, ketika kelas C# generik dibuat dengan menggunakan parameter tipe nilai atau tipe referensi, refleksi dapat mengkuerinya pada waktu berjalan dan jenis aktual serta parameter jenisnya dapat dipastikan.

Lihat juga