Mengapa array mengimplementasikan IList?

141

Lihat definisi kelas System.Array

public abstract class Array : IList, ...

Secara teoritis, saya harus bisa menulis ini sedikit dan bahagia

int[] list = new int[] {};
IList iList = (IList)list;

Saya juga harus dapat memanggil metode apa pun dari iList

 ilist.Add(1); //exception here

Pertanyaan saya bukan mengapa saya mendapatkan pengecualian, melainkan mengapa Array mengimplementasikan IList ?

oleksii
sumber
22
Pertanyaan yang bagus Saya tidak pernah menyukai gagasan antarmuka yang gemuk (itulah istilah teknis untuk desain semacam ini).
Konrad Rudolph
2
Adakah yang benar-benar peduli dengan LSP? Bagi saya, ini agak akademis.
Gabe
13
@ Gabe, maka Anda perlu bekerja dengan basis kode yang lebih besar. Menerapkan perilaku (mewarisi dari antarmuka) dan kemudian mengabaikan hal-hal yang tidak Anda sukai / tidak dapat mendukung mengarah ke bau, dikaburkan, casting dan akhirnya: kode buggy.
Marius
3
@ Dapatkan koleksi yang menyiratkan kemampatan bukan entitas yang terkandung. Anda dapat membuat anggota kelas Anda dari tipe yang mengimplementasikan IRWList <> dan IReadList <>, gunakan jika sebagai IRWList <> secara internal di kelas Anda dan ekspos sebagai IReadList. Ya, Anda harus meletakkan kompleksitas di suatu tempat, tetapi saya tidak melihat bagaimana itu berlaku untuk mengabaikan LSP sebagai prinsip desain yang sangat baik (tidak tahu tentang properti IsReadOnly, yang membuat IList lebih kompleks dari sudut pandang konsumen)
Marius

Jawaban:

94

Karena sebuah array memungkinkan akses cepat dengan indeks, dan IList/ IList<T>adalah satu-satunya antarmuka pengumpulan yang mendukung ini. Jadi mungkin pertanyaan Anda yang sebenarnya adalah "Mengapa tidak ada antarmuka untuk koleksi konstan dengan pengindeks?" Dan untuk itu saya tidak punya jawaban.

Juga tidak ada antarmuka baca untuk koleksi. Dan saya kehilangan mereka bahkan lebih dari ukuran konstan dengan antarmuka pengindeks.

IMO harus ada beberapa antarmuka pengumpulan (generik) tergantung pada fitur koleksi. Dan nama-nama itu seharusnya juga berbeda, Listkarena sesuatu dengan pengindeks adalah IMO yang benar-benar bodoh.

  • Enumerasi saja IEnumerable<T>
  • Hanya baca tetapi tidak ada pengindeks (.Count, .Contains, ...)
  • Resizable tetapi tidak ada pengindeks, yaitu disetel seperti (Tambah, Hapus, ...) saat ini ICollection<T>
  • Hanya baca dengan pengindeks (pengindeks, indeks, ...)
  • Ukuran konstan dengan pengindeks (pengindeks dengan setter)
  • Ukuran variabel dengan pengindeks (Sisipkan, ...) saat ini IList<T>

Saya pikir antarmuka koleksi saat ini adalah desain yang buruk. Tetapi karena mereka memiliki properti yang memberitahu Anda metode mana yang valid (dan ini adalah bagian dari kontrak metode ini) itu tidak melanggar prinsip substitusi.

CodesInChaos
sumber
14
Terima kasih atas jawabannya. Tapi saya lebih suka meninggalkan pertanyaan apa adanya. Alasannya sederhana. Antarmuka adalah kontrak publik. Jika seseorang mengimplementasikannya, ia harus sepenuhnya mengimplementasikan semua anggota, jika tidak maka akan merusak LSP dan umumnya berbau tidak enak, bukan?
oleksii
16
Itu melanggar LSP. Jika tidak daftar. Tambahkan (item) harus menambahkan item ke daftar terlepas dari jenis konkret. Kecuali untuk kasus pengecualian. Dalam implementasi array di melempar pengecualian dalam kasus non-pengecualian, yang di dalamnya sendiri adalah praktik yang buruk
Rune FS
2
@smelch Maaf, tapi Anda salah LSP. Array tidak diimplementasikan adddan karenanya tidak dapat digantikan dengan sesuatu yang berfungsi saat kemampuan itu diperlukan.
Rune FS
7
Saya mengakui bahwa itu secara teknis tidak melanggar LSP hanya karena dokumentasi menyatakan Anda harus memeriksa IsFixedSizedan IsReadOnlyproperti, itu pasti melanggar prinsip Tell, Don't Ask dan Prinsip yang paling tidak mengejutkan . Mengapa menerapkan antarmuka saat Anda hanya akan membuang pengecualian untuk 4 dari 9 metode?
Matius
11
Beberapa waktu telah berlalu sejak pertanyaan awal. Tetapi sekarang dengan .Net 4.5, ada antarmuka tambahan IReadOnlyList dan IReadOnlyCollection .
Tobias
43

Bagian komentar dari dokumentasi untuk IListkata

IList adalah turunan dari antarmuka ICollection dan merupakan antarmuka dasar dari semua daftar non-generik. Implementasi IList terbagi dalam tiga kategori: hanya baca, ukuran tetap, dan ukuran variabel . IList baca-saja tidak dapat dimodifikasi. IList ukuran tetap tidak memungkinkan penambahan atau penghapusan elemen, tetapi memungkinkan modifikasi elemen yang ada. IList ukuran variabel memungkinkan penambahan, penghapusan, dan modifikasi elemen.

Jelas array jatuh ke dalam kategori ukuran tetap, jadi oleh defisi antarmuka masuk akal.

Brian Rasmussen
sumber
4
Saya kira mereka akan berakhir dengan banyak antarmuka. IListFixedSize, IListReadOnly ...
Magnus
9
itu sebenarnya jawaban yang bagus dari sudut pandang dokumentasi. Tetapi bagi saya itu lebih terlihat seperti retasan. Antarmuka harus tipis dan sederhana agar sebuah kelas dapat mengimplementasikan semua anggota.
oleksii
1
@oleksii: Saya setuju. Pengecualian antarmuka dan runtime bukan kombinasi yang paling elegan. Untuk mempertahankannya Arraymenerapkan Addmetode secara eksplisit, yang mengurangi risiko memanggilnya secara tidak sengaja.
Brian Rasmussen
Sampai kita membuat implementasi IListyang melarang modifikasi dan penambahan / penghapusan. Maka dokumentasi tidak lagi benar. : P
Timo
1
@ Magnus - Dalam. Net 4.5, ada antarmuka tambahan IReadOnlyList dan IReadOnlyCollection .
RBT
17

Karena tidak semua ILists bisa berubah (lihat IList.IsFixedSizedan IList.IsReadOnly), dan array tentu berperilaku seperti daftar ukuran tetap.

Jika pertanyaan Anda benar-benar "mengapa ia mengimplementasikan antarmuka non-generik ", maka jawabannya adalah ini ada sebelum generik muncul.

pengguna541686
sumber
10
@oleksii: Tidak, itu tidak merusak LSP, karena antarmuka IList itu sendiri memberi tahu Anda bahwa itu mungkin tidak bisa berubah. Jika ternyata dijamin bisa berubah dan array mengatakan sebaliknya, maka itu akan melanggar aturan.
user541686
Sebenarnya, Array tidak memecah LSP dalam hal generik IList<T>dan tidak memecahnya dalam hal non-generik IList: enterprisecraftsmanship.com/2014/11/22/...
Vladimir
5

Ini adalah warisan yang kami miliki sejak saat tidak jelas bagaimana menangani koleksi hanya baca dan apakah Array hanya dibaca atau tidak. Ada bendera IsFixedSize dan IsReadOnly di antarmuka IList. Bendera IsReadOnly berarti bahwa koleksi tidak dapat diubah sama sekali dan IsFixedSize berarti bahwa koleksi memang memungkinkan modifikasi, tetapi tidak menambahkan atau menghapus item.

Pada saat .Net 4.5 jelas bahwa beberapa antarmuka "perantara" diperlukan untuk bekerja dengan koleksi hanya baca, jadi IReadOnlyCollection<T>dan IReadOnlyList<T>diperkenalkan.

Berikut ini adalah posting blog hebat yang menjelaskan detail: Baca hanya koleksi di .NET

Vladimir
sumber
0

Definisi antarmuka IList adalah "Merupakan koleksi objek non-generik yang dapat diakses secara individual dengan indeks.". Array sepenuhnya memenuhi definisi ini, jadi harus mengimplementasikan antarmuka. Pengecualian saat memanggil metode Tambahkan () adalah "System.NotSupportedException: Koleksi berukuran tetap" dan terjadi karena array tidak dapat meningkatkan kapasitasnya secara dinamis. Kapasitasnya ditentukan selama pembuatan objek array.

meir
sumber
0

Memiliki array yang mengimplementasikan IList (dan secara transitif, ICollection) menyederhanakan mesin Linq2Objects, karena casting IEnumerable ke IList / ICollection juga akan bekerja untuk array.

Sebagai contoh, Count () akhirnya memanggil Array. Panjang di bawah kap, karena itu dilemparkan ke ICollection dan implementasi array mengembalikan Panjang.

Tanpa ini, mesin Linq2Objects tidak akan memiliki perlakuan khusus untuk array dan berkinerja buruk, atau mereka perlu menggandakan kode menambahkan perlakuan kasus khusus untuk array (seperti yang mereka lakukan untuk IList). Mereka pasti memilih untuk membuat array mengimplementasikan IList saja.

Itu pendapat saya tentang "Kenapa".

Herman Schoenfeld
sumber
0

Juga detail implementasi LINQ Pemeriksaan terakhir untuk IList, jika tidak menerapkan daftar, mereka akan membutuhkan 2 pemeriksaan memperlambat semua Panggilan terakhir atau memiliki Terakhir di Array mengambil O (N)

pengguna1496062
sumber