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 jenis generik pertama kali dibangun dengan jenis nilai sebagai parameter, runtime membuat jenis generik khusus dengan parameter atau parameter yang disediakan diganti di lokasi yang sesuai di 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 dari 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 Stack<T> khusus yang dihasilkan. Dalam contoh berikut, dua instans tumpukan bilangan bulat dibuat, dan dua instans tumpukan berbagi satu instans kode Stack<int>:

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

Tetapi, anggaplah kelas Stack<T> lain dengan jenis nilai berbeda seperti long atau struktur yang ditentukan pengguna sebagai parameternya dibuat di 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 native berisi jenis nilai.

Generik bekerja sedikit berbeda untuk jenis referensi. Pertama kali jenis generik dibangun dengan jenis referensi apa pun, runtime membuat jenis generik khusus dengan referensi objek yang digantikan untuk parameter dalam CIL. Kemudian, setiap kali jenis konstruksi dibuat dengan jenis referensi sebagai parameternya, apa pun jenisnya, runtime menggunakan kembali versi khusus yang dibuat sebelumnya dari jenis generik. Hal ini dimungkinkan karena semua referensi berukuran sama.

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

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

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

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

Tidak seperti jenis nilai, versi khusus lain dari kelas Stack<T> tidak dibuat untuk jenis Order. Sebagai gantinya, instans dari versi khusus dari kelas Stack<T> dibuat dan variabel orders diatur untuk mereferensikan instans tersebut. Misalkan Anda kemudian menemukan baris kode untuk membuat tumpukan jenis Customer:

customers = new Stack<Customer>();

Seperti penggunaan kelas Stack<T> sebelumnya yang dibuat dengan menggunakan jenis Order, instans lain dari kelas Stack<T> khusus dibuat. Pointer yang terdapat di dalamnya diatur untuk mereferensikan ke area memori dengan ukuran jenis Customer. Karena jumlah jenis referensi dapat sangat bervariasi dari program ke program, penerapan C# dari 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 jenis nilai atau parameter jenis referensi, refleksi dapat mengkueri kelas C# generik pada run time dan jenis aktual serta parameter jenisnya dapat dipastikan.

Lihat juga