Jadi seperti yang Anda ketahui, array dalam C # mengimplementasikan IList<T>
, di antara antarmuka lainnya. Namun, entah bagaimana, mereka melakukan ini tanpa menerapkan properti Count secara publik IList<T>
! Array hanya memiliki properti Panjang.
Apakah ini contoh terang-terangan dari C # / .NET yang melanggar aturannya sendiri tentang implementasi antarmuka atau apakah saya melewatkan sesuatu?
Array
kelas harus ditulis dalam C #!Array
adalah kelas "ajaib", yang tidak dapat diterapkan di C # atau bahasa lain yang menargetkan .net. Tetapi fitur khusus ini tersedia di C #.Jawaban:
Jawaban baru dalam terang jawaban Hans
Berkat jawaban yang diberikan oleh Hans, kita bisa melihat implementasinya agak lebih rumit dari yang kita kira. Baik compiler dan CLR berusaha keras untuk memberikan kesan bahwa sebuah tipe array diimplementasikan
IList<T>
- tetapi varian array membuat ini lebih rumit. Bertentangan dengan jawaban dari Hans, tipe array (berdimensi tunggal, tetap saja berbasis nol) mengimplementasikan koleksi generik secara langsung, karena tipe dari array tertentu tidakSystem.Array
- itu hanya tipe dasar dari array. Jika Anda menanyakan tipe array apa antarmuka yang didukungnya, itu termasuk tipe generik:Keluaran:
Untuk larik berdimensi tunggal dan berbasis nol, sejauh menyangkut bahasanya , larik juga benar-benar diimplementasikan
IList<T>
. Bagian 12.1.2 dari spesifikasi C # mengatakan demikian. Jadi, apa pun implementasi yang mendasarinya, bahasa tersebut harus berperilaku seolah-olah jenisT[]
implementasinyaIList<T>
sama dengan antarmuka lainnya. Dari perspektif ini, antarmuka yang diimplementasikan dengan beberapa anggota yang secara eksplisit dilaksanakan (sepertiCount
). Itulah penjelasan terbaik di tingkat bahasa untuk apa yang sedang terjadi.Perhatikan bahwa ini hanya berlaku untuk larik berdimensi tunggal (dan larik berbasis nol, bukan karena C # sebagai bahasa mengatakan apa pun tentang larik berbasis non-nol).
T[,]
tidak diimplementasikanIList<T>
.Dari perspektif CLR, sesuatu yang lebih lucu sedang terjadi. Anda tidak bisa mendapatkan pemetaan antarmuka untuk tipe antarmuka generik. Sebagai contoh:
Memberikan pengecualian untuk:
Jadi kenapa anehnya? Yah, saya percaya itu benar-benar karena kovarians array, yang merupakan kutil dalam sistem tipe, IMO. Meskipun
IList<T>
ini tidak kovarian (dan tidak dapat dengan aman), array kovarians memungkinkan ini bekerja:... yang membuatnya terlihat seperti
typeof(string[])
alatIList<object>
, padahal sebenarnya tidak.Spesifikasi CLI (ECMA-335) partisi 1, bagian 8.7.1, memiliki ini:
...
(Itu tidak benar-benar menyebutkan
ICollection<W>
atauIEnumerable<W>
yang saya percaya adalah bug dalam spesifikasi.)Untuk non-varians, spesifikasi CLI sejalan dengan spesifikasi bahasa secara langsung. Dari bagian 8.9.1 partisi 1:
( Vektor adalah larik berdimensi tunggal dengan basis nol.)
Sekarang dalam hal detail implementasi , jelas CLR melakukan beberapa pemetaan yang funky untuk menjaga kompatibilitas penugasan di sini: ketika a
string[]
diminta untuk implementasiICollection<object>.Count
, CLR tidak dapat menanganinya dengan cara yang cukup normal. Apakah ini dihitung sebagai implementasi antarmuka eksplisit? Saya pikir masuk akal untuk memperlakukannya seperti itu, karena kecuali Anda meminta pemetaan antarmuka secara langsung, itu selalu berperilaku seperti itu dari perspektif bahasa.Tentang apa
ICollection.Count
?Sejauh ini saya telah berbicara tentang antarmuka generik, tetapi kemudian ada antarmuka non-generik
ICollection
denganCount
propertinya. Kali ini kita bisa mendapatkan pemetaan antarmuka, dan ternyata antarmuka diimplementasikan langsung olehSystem.Array
. Dokumentasi untukICollection.Count
implementasi properti dalamArray
status yang diimplementasikan dengan implementasi antarmuka eksplisit.Jika ada yang dapat memikirkan cara di mana penerapan antarmuka eksplisit semacam ini berbeda dari penerapan antarmuka eksplisit "normal", saya akan dengan senang hati memeriksanya lebih jauh.
Jawaban lama seputar implementasi antarmuka eksplisit
Terlepas dari hal di atas, yang lebih rumit karena pengetahuan tentang array, Anda masih dapat melakukan sesuatu dengan efek terlihat yang sama melalui implementasi antarmuka eksplisit .
Berikut contoh mandiri sederhana:
sumber
Count
itu baik-baik saja - tetapiAdd
akan selalu melempar, karena array berukuran tetap.Ya, erm tidak, tidak juga. Ini adalah deklarasi untuk kelas Array dalam kerangka .NET 4:
Ini mengimplementasikan System.Collections.IList, bukan System.Collections.Generic.IList <>. Itu tidak bisa, Array tidak generik. Hal yang sama berlaku untuk antarmuka IEnumerable <> dan ICollection <> generik.
Tapi CLR menciptakan tipe array konkret dengan cepat, sehingga secara teknis bisa membuat satu yang mengimplementasikan antarmuka ini. Tetapi, ini bukan perkaranya. Coba kode ini misalnya:
Panggilan GetInterfaceMap () gagal untuk tipe array konkret dengan "Interface not found". Namun cast ke IEnumerable <> bekerja tanpa masalah.
Ini adalah cara mengetik dukun seperti bebek. Ini adalah jenis pengetikan yang sama yang menciptakan ilusi bahwa setiap jenis nilai berasal dari ValueType yang berasal dari Object. Baik compiler dan CLR memiliki pengetahuan khusus tentang tipe array, seperti halnya tipe nilai. Kompilator melihat upaya Anda untuk mentransmisikan ke IList <> dan berkata "oke, saya tahu bagaimana melakukan itu!". Dan memancarkan instruksi IL castclass. CLR tidak memiliki masalah dengannya, ia tahu bagaimana menyediakan implementasi IList <> yang bekerja pada objek array yang mendasarinya. Ini memiliki pengetahuan built-in tentang kelas System.SZArrayHelper yang tersembunyi, pembungkus yang benar-benar mengimplementasikan antarmuka ini.
Yang tidak dilakukan secara eksplisit seperti klaim semua orang, properti Hitung yang Anda tanyakan terlihat seperti ini:
Ya, Anda pasti bisa menyebut komentar itu "melanggar aturan" :) Jika tidak, komentar itu sangat berguna. Dan tersembunyi dengan sangat baik, Anda dapat memeriksanya di SSCLI20, distribusi sumber bersama untuk CLR. Telusuri "IList" untuk melihat di mana substitusi tipe berlangsung. Tempat terbaik untuk melihatnya beraksi adalah metode clr / src / vm / array.cpp, GetActualImplementationForArrayGenericIListMethod ().
Substitusi semacam ini di CLR cukup ringan dibandingkan dengan apa yang terjadi dalam proyeksi bahasa di CLR yang memungkinkan penulisan kode terkelola untuk WinRT (alias Metro). Hampir semua jenis inti .NET diganti di sana. IList <> memetakan ke IVector <> misalnya, tipe yang sepenuhnya tidak terkelola. Itu sendiri merupakan substitusi, COM tidak mendukung tipe generik.
Nah, itulah tadi sekilas tentang apa yang terjadi di balik tirai. Ini bisa menjadi laut yang sangat tidak nyaman, aneh dan asing dengan naga yang hidup di ujung peta. Ini bisa sangat berguna untuk membuat Earth datar dan membuat model gambar berbeda dari apa yang sebenarnya terjadi dalam kode terkelola. Memetakannya ke jawaban favorit semua orang nyaman seperti itu. Yang tidak berfungsi dengan baik untuk tipe nilai (jangan mutasi struct!) Tapi yang ini sangat tersembunyi. Kegagalan metode GetInterfaceMap () adalah satu-satunya kebocoran dalam abstraksi yang dapat saya pikirkan.
sumber
Array
kelas tersebut, yang bukan merupakan tipe dari sebuah array. Ini adalah tipe dasar untuk sebuah array. Sebuah array berdimensi tunggal di C # bisa diterapkanIList<T>
. Dan tipe non-generik pasti dapat mengimplementasikan antarmuka generik ... yang berfungsi karena ada banyak tipe yang berbeda -typeof(int[])
! = Typeof (string []), so
typeof (int []) `implementIList<int>
, dantypeof(string[])
implementIList<string>
.Array
(yang seperti yang Anda tunjukkan, adalah kelas abstrak, jadi tidak mungkin menjadi tipe sebenarnya dari objek array) dan kesimpulan (yang tidak diimplementasikanIList<T>
) adalah IMO yang salah. The cara di mana ia menerapkanIList<T>
tidak biasa dan menarik, saya akan setuju - tapi itu murni sebuah implementasi rinci. Untuk mengklaim bahwaT[]
tidak menerapkanIList<T>
adalah IMO yang menyesatkan. Ini bertentangan dengan spesifikasi dan semua perilaku yang diamati.IList<T>
karenaArray
tidak? Logika itu adalah sebagian besar dari apa yang tidak saya setujui. Di luar itu, saya pikir kita harus menyetujui definisi tentang apa artinya suatu tipe untuk mengimplementasikan antarmuka: menurut pendapat saya, tipe array menampilkan semua fitur yang dapat diamati dari tipe yang diimplementasikanIList<T>
, selainGetInterfaceMapping
. Sekali lagi, bagaimana hal itu dicapai kurang penting bagi saya, sama seperti saya baik-baik saja dengan mengatakan bahwaSystem.String
itu tidak dapat diubah, meskipun detail penerapannya berbeda.IList<T>
agar berfungsi.IList<T>.Count
diimplementasikan secara eksplisit :Ini dilakukan agar ketika Anda memiliki variabel array sederhana, Anda tidak memiliki keduanya
Count
danLength
langsung tersedia.Secara umum, implementasi antarmuka eksplisit digunakan ketika Anda ingin memastikan bahwa suatu tipe dapat digunakan dengan cara tertentu, tanpa memaksa semua konsumen tipe untuk memikirkannya seperti itu.
Edit : Ups, ingatan buruk di sana.
ICollection.Count
diterapkan secara eksplisit. GenerikIList<T>
ditangani seperti yang dijelaskan Hans di bawah ini .sumber
string
).ICollection
mendeklarasikanCount
, dan akan lebih membingungkan jika tipe dengan kata "collection" di dalamnya tidak menggunakanCount
:). Selalu ada trade-off dalam membuat keputusan ini.IList<T>
, meskipun spesifikasi bahasa dan CLI tampak sebaliknya. Saya berani mengatakan cara implementasi antarmuka bekerja di bawah sampul mungkin rumit, tetapi itulah yang terjadi dalam banyak situasi. Apakah Anda juga akan memberikan suara negatif kepada seseorang yang mengatakan bahwaSystem.String
itu tidak dapat diubah, hanya karena cara kerja internal dapat berubah? Untuk semua tujuan praktis - dan tentunya sejauh menyangkut bahasa C # - ini adalah impl.Implementasi antarmuka eksplisit . Singkatnya, Anda menyatakan suka
void IControl.Paint() { }
atauint IList<T>.Count { get { return 0; } }
.sumber
Ini tidak berbeda dengan implementasi antarmuka eksplisit dari IList. Hanya karena Anda mengimplementasikan antarmuka tidak berarti anggotanya perlu muncul sebagai anggota kelas. Ini tidak menerapkan properti Count, hal itu tidak mengekspos pada X [].
sumber
Dengan sumber referensi yang tersedia:
Khususnya bagian ini:
(Penekanan saya)
Sumber (gulir ke atas).
sumber