'IList' ve 'ICollection' ile 'Koleksiyon' arasında geri dönüyor


120

Genel API yöntemlerimden ve özelliklerimden hangi koleksiyon türünü döndürmem gerektiği konusunda kafam karıştı.

Aklımdaki koleksiyonlar IList, ICollectionve Collection.

Bu türlerden birini iade etmek her zaman diğerlerinden daha mı tercih edilir, yoksa özel duruma mı bağlıdır?



Yanıtlar:


71

Genel olarak, olabildiğince genel bir tür döndürmelisiniz, yani, tüketicinin kullanması gereken döndürülen verilerden yeterince bilen biri. Bu şekilde, onu kullanan kodu bozmadan API'nin uygulamasını değiştirme konusunda daha fazla özgürlüğe sahip olursunuz.

IEnumerable<T>Arayüzü dönüş türü olarak da düşünün . Sonuç yalnızca yinelenecekse, tüketicinin bundan fazlasına ihtiyacı yoktur.


25
Ancak, bir Count özelliği dahil olmak üzere ek yardımcı programlar sağlamak için IEnumerable <T> 'yi genişleten ICollection <T>' yi de göz önünde bulundurun. Bu yardımcı olabilir çünkü diziyi birden çok kez taramak zorunda kalmadan dizinin uzunluğunu belirleyebilirsiniz.
retrodrone

11
@retrodrone: ait Aslında uygulama Countyöntemi kontrolleri koleksiyonunun fiili tipi, kullanmak, böylece Lengthveya Countdiziler gibi bilinen bazı türleri için özellikleri ve ICollectionhatta bir olarak temin zaman IEnumerable.
Guffa

8
@Guffa: Bu nota değerli olabilir eğer bir sınıf Thing<T>uygular IList<T>ancak genel olmayan ICollection, daha sonra çağıran IEnumerable<Cat>.Count()bir üzerinde Thing<Cat>hızlı olacaktır ama çağırarak IEnumerable<Animal>.Count(), bulmak bir uygulamasını uzantısı yöntemi arardım beri (yavaş olurdu ve değil ICollection<Cat>). Ancak, sınıf genel olmayanı uygularsa ICollection, IEnumerable<Animal>.Countbunu bulur ve kullanır.
supercat


8
Katılmıyorum. Sahip olduğunuz en zengin türü iade etmek en iyisidir, böylece müşteri isterse onu doğrudan kullanabilir. Bir Listeniz <> varsa, neden yalnızca arayanı ToList () 'i gereksiz yere çağırmaya zorlamak için bir IEnuerable <> döndürür?
zumalifeguard

140

ICollection<T>gibi toplama semantiğini ortaya bir arayüz Add(), Remove()ve Count.

Collection<T>ICollection<T>arayüzün somut bir uygulamasıdır .

IList<T>esasen ICollection<T>rastgele sıra tabanlı erişime sahiptir.

Bu durumda, sonuçlarınızın sıraya dayalı indeksleme (daha sonra kullanın IList<T>) gibi liste anlambilimlerini gerektirip gerektirmediğine veya sırasız bir sonuç "çantasını" döndürmeniz gerekip gerekmediğine (sonra kullanın ) karar vermelisiniz ICollection<T>.


5
Collection<T>uygular IList<T>ve sadece değil ICollection<T>.
CodesInChaos

56
.Net'teki tüm koleksiyonlar sipariş edilmiştir, çünkü IEnumerable<T>sipariş edilmiştir. Ne ayıran IList<T>gelen ICollection<T>bu dizinleri ile işe üyelerine sunmasıdır. Örneğin list[5]çalışır, ancak collection[5]derlenmez.
svick

5
Ayrıca düzenli koleksiyonların uygulanması gerektiğine de katılmıyorum IList<T>. Olmayan çok sayıda düzenli koleksiyon var. IList<T>hızlı indekslenmiş erişim hakkındadır.
CodesInChaos

4
@yoyo .net koleksiyon sınıfları ve arayüzleri basitçe kötü tasarlanmış ve kötü adlandırılmıştır. Onlara neden böyle isim verdiklerini fazla düşünmem.
CodesInChaos

6
@svick: Tüm koleksiyonlar sipariş IEnumerable<T>edildiği için mi sıralanır? Al HashSet<T>- uygular IEnumerable<T>ama açıkça sipariş edilmemiştir. Yani, öğelerin sırası üzerinde hiçbir etkiniz yoktur ve dahili karma tablo yeniden düzenlenirse bu sıra herhangi bir zamanda değişebilir.
Olivier Jacot-Descombes

62

Arasındaki temel fark IList<T>ve ICollection<T>olmasıdır IList<T>dizin kullanarak erişim elemanlarına izin verir. IList<T>Dizi benzeri türleri tanımlar. Bir içindeki ICollection<T>öğelere yalnızca numaralandırma yoluyla erişilebilir. Her ikisi de öğelerin eklenmesine ve silinmesine izin verir.

Yalnızca bir koleksiyonu numaralandırmanız gerekiyorsa, o IEnumerable<T>zaman tercih edilmelidir. Diğerlerine göre iki avantajı vardır:

  1. Koleksiyondaki değişikliklere izin vermez (ancak referans türündeyse öğelerde değil).

  2. Algoritmik olarak oluşturulan ve hiç koleksiyon olmayan numaralandırmalar dahil olmak üzere mümkün olan en geniş kaynak çeşitliliğine izin verir.

Collection<T>esas olarak koleksiyon uygulayıcıları için yararlı olan temel bir sınıftır. Arayüzlerde (API'ler) açığa çıkarırsanız, ondan türetilmeyen birçok yararlı koleksiyon hariç tutulacaktır.


Bir dezavantajı IList<T>dizilerin onu uygulaması, ancak öğe eklemenize veya çıkarmanıza izin vermemesidir (yani dizi uzunluğunu değiştiremezsiniz). Bir IList<T>.Add(item)diziyi çağırırsanız bir istisna atılır . Durum, bunu denemeden önce kontrol edebileceğiniz IList<T>bir Boole özelliği olduğu için biraz etkisiz hale getirilmiştir IsReadOnly. Ama benim gözümde, bu hala kütüphanede bir tasarım hatası . Bu nedenle, List<T>öğe ekleme veya çıkarma olasılığı gerektiğinde doğrudan kullanıyorum .


Kod Analizi (ve FxCop) önerir : somut jenerik koleksiyonu döndürürken, aşağıdakilerden biri iade edilmesi gerektiğini Collection, ReadOnlyCollectionya da KeyedCollection. Ve bu Listiade edilmemelidir.
DavidRR

@DavidRR: Bir listeyi, öğe eklemek veya kaldırmak zorunda olan bir yönteme aktarmak genellikle yararlıdır. Parametreyi olarak yazarsanız IList<T>, yöntemin parametre olarak bir dizi ile çağrılmayacağından emin olmanın bir yolu yoktur.
Olivier Jacot-Descombes

7

IList<T>tüm genel listeler için temel arabirimdir. Sıralı bir koleksiyon olduğu için uygulama, sıralı düzenden ekleme sırasına kadar değişen sıralamaya karar verebilir. Ayrıca Ilist, yöntemlerin listedeki girdileri dizinlerine göre okumasına ve düzenlemesine izin veren Öğe özelliğine sahiptir. Bu, bir konum indeksinde listeye bir değer girmeyi / listeden bir değeri çıkarmayı mümkün kılar.

Ayrıca IList<T> : ICollection<T>, tüm yöntemler ICollection<T>burada uygulama için de mevcuttur.

ICollection<T>tüm genel koleksiyonlar için temel arabirimdir. Boyut, numaralandırıcılar ve senkronizasyon yöntemlerini tanımlar. Bir koleksiyona bir öğe ekleyebilir veya kaldırabilirsiniz, ancak indeks özelliğinin olmaması nedeniyle bunun hangi konumda olacağını seçemezsiniz.

Collection<T>için bir uygulama sağlar IList<T>, IListve IReadOnlyList<T>.

ICollection<T>Bunun yerine daha dar bir arabirim türü kullanırsanız, IList<T>kodunuzu değişikliklere karşı korursunuz. Gibi daha geniş bir arabirim türü kullanırsanız IList<T>, kod değişikliklerini bozma tehlikesi daha yüksektir.

Bir kaynaktan alıntı yapmak ,

ICollection, ICollection<T>: Koleksiyonu değiştirmek istiyorsunuz veya boyutunu önemsiyorsunuz. IList, IList<T>: Koleksiyonu değiştirmek istiyorsunuz ve koleksiyondaki öğelerin sıralanmasını ve / veya konumlandırılmasını önemsiyorsunuz.


5

Bir arayüz türünün iade edilmesi daha geneldir, bu nedenle (özel kullanım durumunuzla ilgili daha fazla bilgi eksikliğinden) buna eğilirim. İndeksleme desteğini ortaya çıkarmak istiyorsanız, seçin IList<T>, aksi ICollection<T>takdirde yeterli olacaktır. Son olarak, döndürülen türlerin salt okunur olduğunu belirtmek istiyorsanız, seçin IEnumerable<T>.

Ve daha önce okumadıysanız, Brad Abrams ve Krzysztof Cwalina "Framework Design Guidelines: Conventions, Deyimler ve Yeniden Kullanılabilir .NET Kitaplıkları için Kalıplar" başlıklı harika bir kitap yazdı ( buradan bir özet indirebilirsiniz ).


IEnumerable<T>salt okunur mu? Elbette, ancak bir depo bağlamında yeniden ayarlanırken, ToList çalıştırıldıktan sonra bile iş parçacığı güvenli değildir. Sorun, orijinal veri kaynağı GC aldığında, IEnumarble.ToList () çok iş parçacıklı bir bağlamda çalışmaz. Doğrusal bir üzerinde çalışır, çünkü işi yaptıktan sonra GC çağrılır, ancak asyncşimdi 4.5+ IEnumarbale'de bir gün kullanmak bir top ağrısı haline geliyor.
Piotr Kula

-3

Bu sorudan çıkan bazı konular var:

  • arayüzler ve sınıflar
  • birkaç benzer sınıf, koleksiyon, liste, diziden hangi belirli sınıf?
  • Ortak sınıflar ve alt öğe ("jenerik") koleksiyonları

Nesne Yönelimli bir API olduğunu vurgulamak isteyebilirsiniz

arayüzler ve sınıflar

Arayüzlerle ilgili fazla tecrübeniz yoksa, derslere bağlı kalmanızı öneririm. Pek çok geliştiricinin gerekli olmasa bile arayüzlere atladığını görüyorum.

Ve, iyi bir sınıf tasarımı yerine, sonunda iyi bir arayüz tasarımına taşınabilecek zayıf bir arayüz tasarımı yapmayı bitirin ...

API'de birçok arayüz göreceksiniz, ancak ihtiyacınız yoksa acele etmeyin.

Sonunda kodunuza arayüzleri nasıl uygulayacağınızı öğreneceksiniz.

birkaç benzer sınıf, koleksiyon, liste, diziden hangi belirli sınıf?

C # (dotnet) içinde birbiriyle değiştirilebilen birkaç sınıf vardır. Daha önce de belirtildiği gibi, "CanBeSortedClass" gibi daha spesifik bir sınıftan bir şeye ihtiyacınız varsa, bunu API'nizde açıkça belirtin.

API kullanıcınızın, sınıfınızın sıralanabileceğini veya öğelere bir biçim uygulayabileceğini gerçekten bilmesi gerekiyor mu? Daha sonra "CanBeSortedClass" veya "ElementsCanBePaintedClass" kullanın, aksi takdirde "GenericBrandClass" kullanın.

Aksi takdirde, daha genel bir sınıf kullanın.

Ortak koleksiyon sınıfları ve alt öğe ("genel") koleksiyonları

Diğer öğeleri içeren sınıflar olduğunu göreceksiniz ve tüm öğelerin belirli bir türde olması gerektiğini belirtebilirsiniz.

Genel Koleksiyonlar , her yeni alt öğe türü için yeni bir koleksiyon oluşturmak zorunda kalmadan birkaç kod uygulaması için aynı koleksiyonu kullanabileceğiniz sınıflardır, örneğin: Koleksiyon .

API kullanıcınız tüm öğeler için aynı olan çok özel bir türe ihtiyaç duyacak mı?

Gibi bir şey kullanın List<WashingtonApple>.

API kullanıcınız birkaç ilgili türe ihtiyaç duyacak mı?

Açığa List<Fruit>API'nızdan için, ve kullanımı List<Orange> List<Banana>, List<Strawberry>içten, nerede Orange, Bananave Strawberrygelen torunları Fruit.

API kullanıcınızın genel bir tür koleksiyonuna ihtiyacı olacak mı?

ListTüm öğelerin olduğu yerde kullanın object.

Şerefe.


7
"Arayüzlerle fazla deneyiminiz yoksa, sınıflara bağlı kalmanızı öneririm" Bu iyi bir tavsiye değil. Bu, bilmediğin bir şey varsa ondan uzak dur demek gibi. Arayüzler son derece güçlüdür ve "Soyutlamalara karşı somutlamalara karşı program" kavramını desteklerler. Ayrıca, türleri taklitler aracılığıyla değiştirebildikleri için birim testi yapmak için oldukça güçlüdürler. Arabirimleri bu yorumların dışında kullanmak için başka harika nedenler var, bu yüzden lütfen onları kesinlikle keşfedin.
atconway

@atconway Düzenli olarak arayüz kullanıyorum, ancak birçok kavram gibi güçlü bir araç, kullanımı kolay olabilir. Ve bazen geliştiricinin buna ihtiyacı olmayabilir. Benim önerim "hiçbir zaman arayüz yapma" değildi, önerim "bu durumda arayüzleri atlayabilirsiniz. Bunu öğrenin, daha sonra en iyi şekilde yararlanabilirsiniz". Şerefe.
umlcat

1
Her zaman bir arayüze programlamayı öneririm. Kodun gevşek bir şekilde bağlı kalmasına yardımcı olur.
mac10688

@ mac10688 Önceki cevabım (atconway) yorumunuz için de geçerlidir.
umlcat
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.