Herkes bana neden C # liste üzerinde IList kullanmak isteyeceğini açıklayabilir misiniz?
İlgili soru: Maruz kalmanın neden kötü olduğu düşünülmektedirList<T>
Herkes bana neden C # liste üzerinde IList kullanmak isteyeceğini açıklayabilir misiniz?
İlgili soru: Maruz kalmanın neden kötü olduğu düşünülmektedirList<T>
Yanıtlar:
Sınıfınızı başkalarının kullanacağı bir kütüphane aracılığıyla sergiliyorsanız, genellikle somut uygulamalar yerine arayüzler aracılığıyla göstermek istersiniz. Bu, daha sonra farklı bir somut sınıf kullanmak için sınıfınızın uygulanmasını değiştirmeye karar verirseniz yardımcı olacaktır. Bu durumda, arayüz değişmediği için kitaplığınızın kullanıcılarının kodlarını güncellemeleri gerekmez.
Sadece dahili olarak kullanıyorsanız, çok fazla umursamazsınız ve kullanmak iyi List<T>
olabilir.
Daha az popüler olan cevap, programcıların yazılımlarının dünya üzerinde yeniden kullanılacağını iddia etmeleridir, projelerin çoğunluğu az miktarda insan tarafından sürdürülecek ve arayüzle ilgili güzel sesler olsa da, kendin.
Mimari astronotlar . Şimdiye kadar zaten kendi .NET çerçevesi olanlara bir şey ekleyen kendi IList yazmak şansı o kadar uzak ki "en iyi uygulamalar" için ayrılmış teorik jöle tots.
Bir röportajda hangisini kullandığınız sorulursa, IList diyorsunuz, gülüyorsunuz ve her ikisi de çok zeki olduğu için kendinize memnun görünüyorlar. Veya halka açık bir API için IList. Umarım anladım.
IEnumerable<string>
içinde List<string>
koleksiyonumu oluşturmak için bir iç destek deposu için kullanabilirim, ancak arayanların yalnızca içeriğini saymasını, eklemesini veya kaldırmasını istemiyorum. Bir arabirimi parametre olarak kabul etmek benzer bir iletiyi iletir: "Bir dizeler koleksiyonuna ihtiyacım var, endişelenmeyin, değiştirmeyeceğim."
Arayüz bir vaattir (veya bir sözleşmedir).
Her zaman vaatlerde olduğu gibi - daha küçük daha iyi .
IList
sözdür. Hangisi daha küçük ve daha iyi bir vaat? Bu mantıklı değil.
List<T>
, çünkü AddRange
arayüzde tanımlanmadı.
IList<T>
tutamayacağı vaatlerde bulunur. Örneğin, salt okunur olması Add
durumunda atabilecek bir yöntem vardır IList<T>
. List<T>
Öte yandan her zaman yazılabilir ve bu yüzden her zaman sözlerini tutar. Ayrıca, .NET 4.6'dan beri, şimdi IReadOnlyCollection<T>
ve IReadOnlyList<T>
neredeyse her zaman daha iyi uyuyor IList<T>
. Ve kovaryantlar. Kaçının IList<T>
!
IList<T>
. Birisi de IReadOnlyList<T>
her yöntemi uygulayıp bir istisna fırlatabilir. Ördek yazmanın tam tersi - buna ördek diyorsunuz, ama bir tane gibi quack olamaz. Ancak derleyici bunu uygulayamasa bile sözleşmenin bir parçası. Ben% 100 katılıyorum yapmak IReadOnlyList<T>
veya IReadOnlyCollection<T>
olsa salt okunur-erişim için kullanılmalıdır.
Bazı insanlar " IList<T>
yerine her zaman kullan" derler List<T>
.
Onlar sizin yöntem imzaları değiştirmek istediğiniz void Foo(List<T> input)
için void Foo(IList<T> input)
.
Bu insanlar yanlış.
Bundan daha nüanslı. IList<T>
Genel arayüzün bir parçası olarak kitaplığınıza dönüyorsanız , gelecekte özel bir liste yapmak için kendinize ilginç seçenekler bırakmış olursunuz. Bu seçeneğe asla ihtiyacınız olmayabilir, ancak bu bir argüman. Bence somut tip yerine arayüzün geri dönmesi için tüm argüman bu. Bahsetmeye değer, ancak bu durumda ciddi bir kusuru var.
Küçük bir karşı strateji olarak, her arayanın List<T>
yine de ihtiyacı olduğunu görebilirsiniz ve arama kodu.ToList()
Ama çok daha önemlisi, bir parametre olarak bir IList kabul eden çünkü eğer daha iyi, dikkatli olurum IList<T>
ve List<T>
aynı şekilde davranmazlar. Adına benzerliğine rağmen ve bir paylaşım rağmen arayüz onlar do not aynı maruz sözleşme .
Bu yönteme sahip olduğunuzu varsayalım:
public Foo(List<int> a)
{
a.Add(someNumber);
}
Yararlı bir meslektaşım kabul yöntemi "refactors" IList<int>
.
Kodunuz şimdi kırılmıştır, çünkü int[]
uygulanmaktadır IList<int>
, ancak sabit boyuttadır. Sözleşmesi ICollection<T>
(temeli IList<T>
) IsReadOnly
, koleksiyona öğe eklemeye veya koleksiyondan öğe çıkarmaya çalışmadan önce bayrağı kontrol etmek için onu kullanan kodu gerektirir . İçin sözleşme List<T>
yapmaz.
Liskov İkame Prensibi (basitleştirilmiş), türetilmiş bir tipin, herhangi bir ön koşul veya son koşul olmaksızın baz tip yerine kullanılabilmesi gerektiğini belirtir.
Bu Liskov ikame ilkesini ihlal ediyor gibi geliyor.
int[] array = new[] {1, 2, 3};
IList<int> ilist = array;
ilist.Add(4); // throws System.NotSupportedException
ilist.Insert(0, 0); // throws System.NotSupportedException
ilist.Remove(3); // throws System.NotSupportedException
ilist.RemoveAt(0); // throws System.NotSupportedException
Ama öyle değil. Bunun cevabı, örnekte IList <T> / ICollection <T> 'nin yanlış kullanılmasıdır. Bir ICollection <T> kullanıyorsanız, IsReadOnly bayrağını kontrol etmeniz gerekir.
if (!ilist.IsReadOnly)
{
ilist.Add(4);
ilist.Insert(0, 0);
ilist.Remove(3);
ilist.RemoveAt(0);
}
else
{
// what were you planning to do if you were given a read only list anyway?
}
Birisi size bir Array veya List iletirse, her seferinde bayrağı kontrol edip bir yedeğiniz varsa kodunuz iyi çalışır ... Ama gerçekten; bunu kim yapar? Yöntemin ek üye alabilecek bir listeye ihtiyacı olup olmadığını önceden bilmiyor musun; bunu yöntem imzasında belirtmiyor musunuz? Eğer salt okunur bir listeden geçtiyseniz tam olarak ne yapacaktınız int[]
?
/ Kullanarak doğru bir List<T>
kodu koyabilirsiniz . Sen olamaz Bir yerine garanti / koduna kullanımları olduğunu .IList<T>
ICollection<T>
IList<T>
ICollection<T>
List<T>
Somut türler yerine soyutlamaları kullanmak için birçok argümanda Tek Sorumluluk İlkesi / Arayüz Ayrışma İlkesine bir itiraz var - mümkün olan en dar arayüze bağlı. Çoğu durumda, a kullanıyorsanız List<T>
ve bunun yerine daha dar bir arayüz kullanabileceğinizi düşünüyorsanız - neden olmasın IEnumerable<T>
? Öğe eklemeniz gerekmiyorsa, bu genellikle daha uygun olur. Koleksiyona eklemeniz gerekiyorsa, beton türünü kullanın List<T>
.
Benim için IList<T>
(ve ICollection<T>
) .NET çerçevenin en kötü parçasıdır. IsReadOnly
en az sürpriz ilkesini ihlal eder. Array
Öğe eklemeye, eklemeye veya kaldırmaya asla izin vermeyen gibi bir sınıf, Ekle, Ekle ve Kaldır yöntemleriyle bir arabirim uygulamamalıdır. (ayrıca bkz. /software/306105/implementing-an-interface-when-you-dont-need-one-of-the-perperties )
Mı IList<T>
kuruluşunuz için uygun? Bir iş arkadaşı sizden kullanmak IList<T>
yerine bir yöntem imzasını değiştirmenizi isterse List<T>
, onlara bir öğeye nasıl öğe ekleyeceklerini sorun IList<T>
. Eğer bilmiyorlarsa IsReadOnly
(ve çoğu insan bilmiyorsa), kullanmayın IList<T>
. Hiç.
IsReadOnly bayrağının ICollection <T> öğesinden geldiğini ve öğelerin koleksiyona eklenip eklenemeyeceğini gösterdiğini unutmayın; ancak sadece şeyleri gerçekten karıştırmak için, değiştirilip değiştirilemeyeceğini göstermez, bu da Diziler (IsReadOnlys == true döndüren) durumunda olabilir.
IsReadOnly hakkında daha fazla bilgi için bkz . ICollection <T> msdn tanımı .IsReadOnly
IsReadOnly
olduğu true
bir için IList
, hala mevcut üyelerinde değişiklik yapabilir! Belki de bu mülkün adı olmalı IsFixedSize
?
Array
olarak IList
geçirilerek test edildi , bu yüzden şu anki üyeleri değiştirebilirim)
IReadOnlyCollection<T>
ve IReadOnlyList<T>
neredeyse her zaman daha iyi uyuyor, IList<T>
ancak tehlikeli tembel anlambilimine sahip değiliz IEnumerable<T>
. Ve kovaryantlar. Kaçının IList<T>
!
List<T>
, bir tamsayı dizini kullanılarak IList<T>
doğrusal bir diziyle aynı şekilde ele alınabilen bir kap olan özel bir uygulamasıdır T[]
. Belirttiğiniz zaman IList<T>
yöntemin argüman türü olarak, yalnızca kabın belirli yetenekleri gerektiğini belirtin.
Örneğin, arabirim belirtimi, kullanılacak belirli bir veri yapısını zorunlu kılmaz. Öğesinin uygulanması, List<T>
öğelere doğrusal bir dizi olarak erişmek, bunları silmek ve eklemek için aynı performansa gerçekleşir. Bununla birlikte, bunun yerine bağlantılı bir liste tarafından desteklenen ve sonuna öğe eklemenin daha ucuz (sabit zamanlı), ancak rastgele erişimin çok daha pahalı olduğu bir uygulama hayal edebilirsiniz. (.NET geldiğini hatırlatırız LinkedList<T>
yok değil uygulamak IList<T>
.)
Bu örnek ayrıca, bağımsız değişken listesinde arabirimi değil, uygulamayı belirtmeniz gerektiğinde bazı durumlar olabileceğini bildirir: Bu örnekte, belirli bir erişim performansı özelliği istediğinizde. Bu genellikle bir konteynerin belirli bir uygulaması için garanti edilir ( List<T>
dokümantasyon: " IList<T>
Boyutu gerektiği gibi dinamik olarak arttırılan bir dizi kullanarak jenerik arayüzü uygular .").
Ayrıca, ihtiyacınız olan en az işlevselliği ortaya çıkarmayı düşünebilirsiniz. Örneğin. Listenin içeriğini değiştirmek gerekmez, muhtemelen kullanmayı düşünmelisiniz IEnumerable<T>
ki, IList<T>
uzanır.
LinkedList<T>
vs almak List<T>
vs IList<T>
tüm kod çağrılan gereken performans garantileri bir şey iletişim.
Soruyu biraz geriye çevirirdim, arayüzü somut uygulama üzerinde neden kullanmanız gerektiğini gerekçelendirmek yerine, arayüz yerine beton uygulamasını neden kullanacağınızı gerekçelendirmeye çalışın. Gerekçelendiremezseniz, arayüzü kullanın.
TDD ve OOP prensibi genellikle bir uygulama değil bir arayüze programlamadır.
Bu özel durumda, aslında bir dil yapısından bahsettiğiniz için, genellikle önemli olmayan özel bir dilden değil, örneğin List'in ihtiyacınız olan bir şeyi desteklemediğini bulduğunuzu söyleyin. Uygulamanın geri kalanında IList kullandıysanız, Listeyi kendi özel sınıfınızla genişletebilir ve yine de yeniden düzenleme yapmadan bunu geçebilirsiniz.
Bunu yapmanın maliyeti minimumdur, neden daha sonra kendinizi baş ağrısından kurtarmıyorsunuz? Arayüz prensibi tamamen bununla ilgilidir.
public void Foo(IList<Bar> list)
{
// Do Something with the list here.
}
Bu durumda, IList <Bar> arabirimini uygulayan herhangi bir sınıftan geçebilirsiniz. Bunun yerine List <Bar> öğesini kullandıysanız, yalnızca List <Bar> örneği geçirilebilir.
IList <Bar> yolu List <Bar> yolundan daha gevşek bir şekilde bağlıdır.
Listenin IList sorularına (veya cevaplarına) karşı hiçbirinin imza farkından bahsetmediğini varsayarak. (Bu yüzden bu soruyu SO'da aradım!)
İşte List'in içerdiği, IList'te bulunmayan, en azından .NET 4.5'ten itibaren (2015 dolaylarında) yöntemler
Reverse
ve ToArray
IList <T> 'nin türetdiği IEnumerable <T> arayüzü için genişletme yöntemleridir.
List
diğer uygulamalarda değil , s'de anlamlı olması IList
.
Uygulamaların üzerinde arabirimlerin kullanılması için en önemli durum API'nizin parametrelerine yöneliktir. API'niz bir List parametresi alırsa, onu kullanan herkesin List'i kullanması gerekir. Parametre tipi IList ise, arayanın çok daha fazla özgürlüğü vardır ve daha önce hiç duymadığınız sınıfları kullanabilir, bu da kodunuz yazıldığında bile olmayabilir.
Ne .NET eğer 5.0 cümledeki System.Collections.Generic.List<T>
için System.Collection.Generics.LinearList<T>
. .NET her zaman adın sahibidir List<T>
ancak IList<T>
bir sözleşme olduğunu garanti eder . Yani IMHO biz (en azından I) birinin adını kullanmamalıyız (bu durumda .NET olsa da) ve daha sonra sorun yaşarız.
Kullanılması durumunda IList<T>
, arayan her zaman işe yarayan şeylerdir ve uygulayıcı, alttaki koleksiyonu herhangi bir alternatif somut uygulamaya dönüştürmekte özgürdür.IList
Tüm kavramlar temel olarak yukarıdaki somut uygulamalarda neden arayüz kullanıldığına dair cevapların çoğunda belirtilmiştir.
IList<T> defines those methods (not including extension methods)
IList<T>
MSDN bağlantısı
List<T>
bu dokuz yöntemi (uzatma yöntemleri dahil değil) uygular, bunun üzerine uygulamanızda hangisinin kullanılacağını dikkate alan yaklaşık 41 genel yöntem vardır.
List<T>
MSDN bağlantısı
Çünkü bir IList ya da bir ICollection tanımlamak, arayüzlerinizin diğer uygulamalarına açılabilir.
Bir IList veya ICollection'daki bir sipariş koleksiyonunu tanımlayan bir IOrderRepository'ye sahip olmak isteyebilirsiniz. Daha sonra, IListiniz veya ICollection'ınız tarafından tanımlanan "kurallara" uydukları sürece siparişlerin bir listesini sağlamak için farklı uygulama türlerine sahip olabilirsiniz.
IList <> diğer posterlerin tavsiyelerine göre neredeyse her zaman tercih edilir, ancak WCF DataContractSerializer ile birden fazla serileştirme / serileştirme döngüsü boyunca bir IList <> çalıştırırken .NET 3.5 sp 1'de bir hata olduğunu unutmayın .
Şimdi bu hatayı düzeltmek için bir SP var: KB 971030
Arayüz en azından beklediğiniz yöntemleri almanızı sağlar ; arayüzün tanımının farkında olmak yani. arabirimi devralan herhangi bir sınıf tarafından uygulanacak tüm soyut yöntemler. bu nedenle, bazıları, bazı ek işlevler için arayüzden devralınanların yanı sıra birkaç yöntemle kendi başına büyük bir sınıf yaparsa ve bunlar sizin için bir faydası yoksa, bir alt sınıfa referans kullanmak daha iyidir (bu durumda arayüz) ve buna somut sınıf nesnesini atayın.
ek avantaj, somut sınıf yöntemlerinden sadece birkaçına abone olduğunuzda ve somut sınıf bulunduğunuz arayüzden miras kaldığı sürece kodunuzda somut sınıftaki herhangi bir değişiklikten güvenli olmasıdır. kullanarak. böylece sizin için güvenliği ve somut sınıfını değiştirmek veya daha fazla işlevsellik eklemek için somut uygulama yazan kodlayıcıya özgürlük.
Bu argümana, bir uygulamaya değil, bir Arayüz'e karşı programladığı söylenen tamamen OO yaklaşımından biri de dahil olmak üzere çeşitli açılardan bakabilirsiniz. Bu düşünce ile IList'i kullanmak, etrafta dolaşmak ve sıfırdan tanımladığınız Arayüzleri kullanmakla aynı prensibi izler. Genel olarak bir Arayüz tarafından sağlanan ölçeklenebilirlik ve esneklik faktörlerine de inanıyorum. IList <T> uygulayan bir sınıfın genişletilmesi veya değiştirilmesi gerekiyorsa, tüketen kodun değiştirilmesi gerekmez; IList Arayüz sözleşmesinin neye bağlı olduğunu bilir. Ancak, değişen bir sınıfta somut bir uygulama ve Liste <T> kullanmak, arama kodunun da değiştirilmesine neden olabilir. Bunun nedeni, IList <T> 'e bağlı bir sınıfın <T> Listesi kullanılarak somut bir tür tarafından garanti edilmeyen belirli bir davranışı garanti etmesidir.
Ayrıca, bir sınıfta List <T> uygulamasının varsayılan uygulamasını değiştirmek gibi bir şey yapma gücüne sahip olmak, örneğin .Add, .Remove veya başka bir IList yöntemi için geliştiriciye çok fazla esneklik ve güç verir, aksi takdirde önceden tanımlanmış Listeye göre <T>
Genellikle iyi bir yaklaşım, IList'i herkese açık API'nızda (uygun olduğunda ve liste semantiği gerektiğinde) kullanmak ve daha sonra API'yı uygulamak için dahili olarak Liste yapmaktır. Bu, sınıfınızı kullanan kodu bozmadan farklı bir IList uygulamasına geçmenizi sağlar.
Sınıf adı Listesi sonraki .net çerçevesinde değiştirilebilir, ancak arabirim sözleşme olduğu için arabirim asla değişmeyecektir.
API'nız yalnızca foreach döngülerinde vb. Kullanılacaksa, bunun yerine yalnızca IEnumerable'u açığa çıkarmayı düşünebilirsiniz.