Saya bingung tentang jenis koleksi mana yang harus saya kembalikan dari metode dan properti API publik saya.
Koleksi yang ada dalam pikiran saya adalah IList
, ICollection
dan Collection
.
Apakah mengembalikan salah satu jenis ini selalu lebih disukai daripada yang lain, atau apakah itu tergantung pada situasi tertentu?
c#
.net
generics
collections
Rocky Singh
sumber
sumber
Jawaban:
Secara umum, Anda harus mengembalikan tipe yang seumum mungkin, yaitu yang cukup mengetahui data yang dikembalikan yang perlu digunakan konsumen. Dengan begitu, Anda memiliki kebebasan yang lebih besar untuk mengubah implementasi API, tanpa merusak kode yang menggunakannya.
Pertimbangkan juga
IEnumerable<T>
antarmuka sebagai tipe kembali. Jika hasilnya hanya akan diulang, konsumen tidak membutuhkan lebih dari itu.sumber
Count
metode ini memeriksa tipe aktual dari koleksi, jadi ia akan menggunakan propertiLength
orCount
untuk beberapa tipe yang dikenal seperti array danICollection
bahkan ketika Anda memasukkannya sebagai sebuahIEnumerable
.Thing<T>
mengimplementasikanIList<T>
tetapi bukan yang non-generikICollection
, maka pemanggilanIEnumerable<Cat>.Count()
aThing<Cat>
akan cepat, tetapi pemanggilanIEnumerable<Animal>.Count()
akan lambat (karena metode ekstensi akan mencari, dan tidak menemukan, implementasi dariICollection<Cat>
). Jika kelas mengimplementasikan non-generikICollection
, bagaimanapun,IEnumerable<Animal>.Count
akan menemukan dan menggunakan itu.ICollection<T>
adalah sebuah antarmuka yang mengekspos semantik koleksi sepertiAdd()
,Remove()
, danCount
.Collection<T>
adalah implementasi konkret dariICollection<T>
antarmuka.IList<T>
pada dasarnya adalahICollection<T>
dengan akses berbasis pesanan acak.Dalam kasus ini, Anda harus memutuskan apakah hasil Anda memerlukan semantik daftar seperti pengindeksan berbasis urutan (lalu gunakan
IList<T>
) atau apakah Anda hanya perlu mengembalikan "kantong" hasil yang tidak berurutan (lalu gunakanICollection<T>
).sumber
Collection<T>
mengimplementasikanIList<T>
dan tidak hanyaICollection<T>
.IEnumerable<T>
dipesan. Yang membedakanIList<T>
dariICollection<T>
adalah bahwa ia menawarkan anggota untuk bekerja dengan indeks. Misalnyalist[5]
berfungsi, tetapicollection[5]
tidak dapat dikompilasi.IList<T>
. Ada banyak koleksi pesanan yang tidak.IList<T>
adalah tentang akses cepat yang diindeks.IEnumerable<T>
dipesan? AmbilHashSet<T>
- itu mengimplementasikanIEnumerable<T>
tetapi jelas tidak dipesan. Misalnya, Anda tidak memiliki pengaruh pada urutan item dan urutan ini dapat berubah kapan saja, jika tabel hash internal diatur ulang.Perbedaan utama antara
IList<T>
andICollection<T>
is yangIList<T>
memungkinkan Anda mengakses elemen melalui indeks.IList<T>
mendeskripsikan tipe seperti array. Elemen dalam sebuahICollection<T>
hanya dapat diakses melalui pencacahan. Keduanya memungkinkan penyisipan dan penghapusan elemen.Jika Anda hanya perlu menghitung koleksi, maka
IEnumerable<T>
lebih disukai. Ini memiliki dua keunggulan dibandingkan yang lain:Ini melarang perubahan pada koleksi (tetapi tidak pada elemen, jika mereka adalah tipe referensi).
Ini memungkinkan variasi sumber terbesar, termasuk enumerasi yang dihasilkan secara algoritmik dan bukan merupakan kumpulan sama sekali.
Collection<T>
adalah kelas dasar yang berguna terutama untuk pelaksana koleksi. Jika Anda mengeksposnya di antarmuka (API), banyak koleksi berguna yang tidak berasal darinya akan dikecualikan.Salah satu kelemahannya
IList<T>
adalah bahwa array mengimplementasikannya tetapi tidak mengizinkan Anda untuk menambah atau menghapus item (misalnya, Anda tidak dapat mengubah panjang array). Pengecualian akan diberikan jika Anda memanggilIList<T>.Add(item)
sebuah array. Situasi ini agak mereda karenaIList<T>
memiliki properti BooleanIsReadOnly
yang dapat Anda periksa sebelum mencoba melakukannya. Tapi di mata saya, ini masih cacat desain di perpustakaan . Oleh karena itu, saya menggunakanList<T>
secara langsung, ketika kemungkinan untuk menambah atau menghapus item diperlukan.sumber
Collection
,ReadOnlyCollection
atauKeyedCollection
. Dan ituList
tidak boleh dikembalikan.IList<T>
, tidak ada cara untuk memastikan bahwa metode tersebut tidak akan dipanggil dengan array sebagai parameter.IList<T>
adalah antarmuka dasar untuk semua daftar umum. Karena ini adalah koleksi yang dipesan, implementasi dapat memutuskan urutannya, mulai dari urutan yang diurutkan hingga urutan penyisipan. Selain itu,Ilist
memiliki properti Item yang memungkinkan metode untuk membaca dan mengedit entri dalam daftar berdasarkan indeksnya. Ini memungkinkan untuk menyisipkan, menghapus nilai ke / dari daftar pada indeks posisi.Juga karena
IList<T> : ICollection<T>
, semua metode dariICollection<T>
juga tersedia di sini untuk diimplementasikan.ICollection<T>
adalah antarmuka dasar untuk semua koleksi umum. Ini mendefinisikan ukuran, enumerator dan metode sinkronisasi. Anda dapat menambah atau menghapus item ke dalam koleksi tetapi Anda tidak dapat memilih di posisi mana hal itu terjadi karena tidak adanya properti indeks.Collection<T>
menyediakan implementasi untukIList<T>
,IList
danIReadOnlyList<T>
.Jika Anda menggunakan jenis antarmuka yang lebih sempit seperti
ICollection<T>
daripadaIList<T>
, Anda melindungi kode Anda dari perubahan yang melanggar. Jika Anda menggunakan tipe antarmuka yang lebih luas sepertiIList<T>
, Anda lebih berisiko melanggar perubahan kode.Mengutip dari sebuah sumber ,
sumber
Mengembalikan jenis antarmuka lebih umum, jadi (kurang informasi lebih lanjut tentang kasus penggunaan spesifik Anda) saya akan condong ke arah itu. Jika Anda ingin mengekspos dukungan pengindeksan, pilih
IList<T>
, jika tidakICollection<T>
akan cukup. Terakhir, jika Anda ingin menunjukkan bahwa tipe yang dikembalikan hanya baca, pilihIEnumerable<T>
.Dan, jika Anda belum membacanya sebelumnya, Brad Abrams dan Krzysztof Cwalina menulis buku hebat berjudul "Framework Design Guidelines: Conventions, Idiom, and Patterns for Reusable .NET Libraries" (Anda dapat mengunduh intisari dari sini ).
sumber
IEnumerable<T>
apakah hanya baca? Tentu, tetapi ketika melakukan retuning dalam konteks repositori, bahkan setelah menjalankan ToList tidak aman untuk thread. Masalahnya adalah ketika sumber data asli mendapatkan GC maka IEnumarble.ToList () tidak berfungsi dalam konteks multi-utas. Ia bekerja pada satu linier karena GC dipanggil setelah Anda melakukan pekerjaan tetapi menggunakanasync
sekarang hari di 4.5+ IEnumarbale menjadi sakit bola.Ada beberapa hal yang muncul dari pertanyaan ini:
Anda mungkin ingin menyoroti bahwa itu adalah API Berorientasi Objek
antarmuka versus kelas
Jika Anda tidak memiliki banyak pengalaman dengan antarmuka, saya sarankan tetap mengikuti kelas. Saya melihat banyak kali pengembang beralih ke antarmuka, meskipun itu tidak perlu.
Dan, akhiri dengan melakukan desain antarmuka yang buruk, alih-alih, desain kelas yang baik, yang pada akhirnya dapat bermigrasi ke desain antarmuka yang baik ...
Anda akan melihat banyak antarmuka di API, tetapi, jangan terburu-buru, jika Anda tidak membutuhkannya.
Anda akhirnya akan belajar bagaimana menerapkan antarmuka, ke kode Anda.
kelas tertentu yang mana, dari beberapa kelas yang sama, koleksi, daftar, larik?
Ada beberapa kelas di c # (dotnet) yang dapat dipertukarkan. Seperti yang telah disebutkan, jika Anda membutuhkan sesuatu dari kelas yang lebih spesifik, seperti "CanBeSortedClass", buatlah itu eksplisit di API Anda.
Apakah pengguna API Anda benar-benar perlu tahu, bahwa kelas Anda dapat diurutkan, atau menerapkan beberapa format ke elemen? Kemudian gunakan "CanBeSortedClass" atau "ElementsCanBePaintedClass", jika tidak gunakan "GenericBrandClass".
Jika tidak, gunakan kelas yang lebih umum.
Kelas koleksi umum versus koleksi subitem ("generik")
Anda akan menemukan bahwa ada kelas yang berisi elemen lain, dan Anda dapat menentukan bahwa semua elemen harus dari tipe tertentu.
Koleksi Generik adalah kelas-kelas yang Anda dapat menggunakan koleksi yang sama, untuk beberapa aplikasi kode, tanpa harus membuat koleksi baru, untuk setiap jenis subitem baru, seperti ini: Koleksi .
Apakah pengguna API Anda membutuhkan tipe yang sangat spesifik, sama untuk semua elemen?
Gunakan sesuatu seperti
List<WashingtonApple>
.Apakah pengguna API Anda akan membutuhkan beberapa jenis terkait?
Ekspos
List<Fruit>
untuk API Anda, dan gunakanList<Orange>
List<Banana>
, secaraList<Strawberry>
internal, dari manaOrange
,Banana
danStrawberry
merupakan turunannyaFruit
.Apakah pengguna API Anda akan membutuhkan koleksi tipe generik?
Gunakan
List
, di mana semua item beradaobject
.Bersulang.
sumber