Ini adalah pertanyaan dari perspektif internal kompiler.
Saya tertarik pada obat generik, bukan template (C ++), jadi saya menandai pertanyaan dengan C #. Bukan Java, karena AFAIK generik dalam kedua bahasa berbeda dalam implementasinya.
Ketika saya melihat bahasa tanpa generik, itu sangat mudah, Anda dapat memvalidasi definisi kelas, menambahkannya ke hierarki dan hanya itu.
Tetapi apa yang harus dilakukan dengan kelas generik, dan yang lebih penting bagaimana menangani referensi untuk itu? Cara memastikan bahwa bidang statis adalah singular per instance (yaitu setiap kali parameter generik diselesaikan).
Katakanlah saya melihat panggilan:
var x = new Foo<Bar>();
Apakah saya menambahkan Foo_Bar
kelas baru ke hierarki?
Pembaruan: Sejauh ini saya hanya menemukan 2 posting yang relevan, tetapi bahkan mereka tidak masuk ke banyak detail dalam arti "bagaimana melakukannya sendiri":
Jawaban:
Setiap instantiasi generik memiliki salinan sendiri dari MethodTable (dinamai membingungkan), yang merupakan tempat bidang statis disimpan.
Saya tidak yakin itu berguna untuk memikirkan hirarki kelas sebagai beberapa struktur yang sebenarnya ada saat runtime, itu lebih merupakan konstruksi logis.
Tetapi jika Anda mempertimbangkan MethodTable, masing-masing dengan pointer tidak langsung ke kelas dasarnya, untuk membentuk hierarki ini, maka ya, ini menambahkan kelas baru ke hierarki.
sumber
Foo<string>
dan mereka tidak akan menghasilkan dua contoh bidang statisFoo
.Saya melihat dua pertanyaan konkret yang sebenarnya di sana. Anda mungkin ingin mengajukan pertanyaan terkait lainnya (sebagai pertanyaan terpisah dengan tautan kembali ke pertanyaan ini) untuk mendapatkan pemahaman penuh.
Bagaimana bidang statis diberikan instance terpisah per instance generik?
Nah, untuk anggota statis yang tidak terkait dengan parameter tipe generik, ini cukup mudah (gunakan kamus yang dipetakan dari parameter generik ke nilai).
Anggota (statis atau tidak) yang terkait dengan parameter tipe dapat ditangani melalui penghapusan tipe. Cukup gunakan apa pun kendala terkuat (sering
System.Object
). Karena informasi jenis dihapus setelah pemeriksaan jenis kompiler, itu berarti bahwa pemeriksaan tipe runtime tidak diperlukan (walaupun antarmuka gips mungkin masih ada saat runtime).Apakah setiap instance generik muncul secara terpisah dalam hierarki tipe?
Tidak dalam .NET generics. Keputusan dibuat untuk mengecualikan pewarisan dari parameter tipe, jadi ternyata semua instance generik menempati tempat yang sama dalam hierarki tipe.
Ini mungkin keputusan yang bagus, karena kegagalan mencari nama dari kelas dasar akan sangat mengejutkan.
sumber
Foo<int>
danFoo<string>
akan memukul data yang sama denganFoo
kendala w / o.List<string>
danList<Form>
, maka karena secaraList<T>
internal memiliki anggota tipeT[]
dan tidak ada kendalaT
, maka apa yang akan Anda dapatkan adalah kode mesin yang memanipulasiobject[]
. Namun, karena hanyaT
instance yang dimasukkan ke dalam array, semua yang keluar dapat dikembalikan sebagaiT
tanpa tipe check tambahan. Di sisi lain, jika Anda punyaControlCollection<T> where T : Control
, maka array internalT[]
akan menjadiControl[]
.Cara umum di ujung depan kompiler adalah memiliki dua jenis instance, tipe generik (
List<T>
) dan tipe generik terikat (List<Foo>
). Tipe generik menentukan fungsi apa yang ada, bidang apa, dan memiliki referensi tipe generik di mana punT
digunakan. Tipe generik terikat berisi referensi ke tipe generik, dan seperangkat argumen tipe. Itu memiliki informasi yang cukup bagi Anda untuk kemudian menghasilkan tipe konkret, mengganti referensi tipe generik denganFoo
atau apa pun jenis argumennya. Perbedaan semacam ini penting ketika Anda melakukan inferensi tipe dan perlu disimpulkanList<T>
dibandingkanList<Foo>
.Alih-alih memikirkan generik seperti templat (yang membangun berbagai implementasi secara langsung), mungkin akan lebih membantu untuk menganggapnya sebagai konstruktor tipe bahasa fungsional (di mana argumen generik seperti argumen menjadi fungsi yang memberi Anda tipe).
Sedangkan untuk bagian belakang, saya tidak benar-benar tahu. Semua pekerjaan saya dengan obat-obatan generik menargetkan CIL sebagai backend, jadi saya bisa mengkompilasinya menjadi obat generik yang didukung di sana.
sumber
List<T>
memiliki tipe nyata (definisi), sementaraList<Foo>
(terima kasih atas bagian terminologi juga) dengan pendekatan saya memegang deklarasiList<T>
(tentu saja sekarang terikat padaFoo
bukannyaT
).