IList <T> yerine her zaman IEnumerable <T> döndürmeli miyim?


98

DAL'ımı veya bir dizi öğe döndüren başka bir kodu yazarken, her zaman return ifademi yapmalı mıyım:

public IEnumerable<FooBar> GetRecentItems()

veya

public IList<FooBar> GetRecentItems()

Şu anda, kodumda IEnumerable'ı olabildiğince kullanmaya çalışıyorum, ancak bunun en iyi uygulama olup olmadığından emin değilim? Doğru görünüyordu çünkü en genel veri türünü döndürürken hala ne yaptığını açıklıyordum, ama belki de bunu yapmak doğru değil.


1
Sorduğunuz List <T> mi yoksa IList <T> mi? Başlık ve soru farklı şeyler söylüyor ...
Fredrik Mörk

Her zaman mümkün olan kullanıcı arayüzü IEnumerable veya Ilist, concerete tipi instaead.
Usman Masood

2
Koleksiyonları Liste <T> olarak döndürüyorum. IEnumberable <T> 'yi döndürme gereğini görmüyorum çünkü bunu List <T>' den çıkarabilirsin
Chuck Conway


Yanıtlar:


45

Gerçekten neden bu belirli arayüzü kullandığınıza bağlı.

Örneğin, IList<T>içinde bulunmayan birkaç yöntemi vardır IEnumerable<T>:

  • IndexOf(T item)
  • Insert(int index, T item)
  • RemoveAt(int index)

ve Özellikler:

  • T this[int index] { get; set; }

Bu yöntemlere herhangi bir şekilde ihtiyacınız varsa, o zaman elbette geri dönün IList<T>.

Ayrıca, IEnumerable<T>sonucunuzu tüketen yöntem bir bekliyorsa IList<T>, CLR'yi gereken dönüştürmeleri dikkate almaktan kurtaracak ve böylece derlenen kodu optimize edecektir.


2
@Jon FDG, koleksiyon türleri için bir dönüş değeri olarak Collection <T> veya ReadOnlyCollection <T> kullanmanızı önerir.
Sam Saffron

Son cümlenizde "CLR'yi kurtaracak" derken neyi kastettiğiniz konusunda net değilsiniz. IEnumerable vs IList kullanarak onu ne kurtaracak? Bunu daha açık hale getirebilir misin?
PositiveGuy

1
@CoffeeAddict Bu cevaptan üç yıl sonra haklı olduğunu düşünüyorum - bu son kısım belirsiz. Parametre olarak bir IList <T> bekleyen bir yöntem bir IEnumerable <T> alırsa, IEnumerable'ın yeni bir List <T> veya başka bir IList <T> uygulayıcısına manuel olarak sarılması gerekir ve bu çalışma sizin için CLR. Tersi - bir IEnumerable <T> 'nin bir IList <T> almasını bekleyen bir yöntem, bazı kutudan çıkarma işlemi yapmak zorunda kalabilir , ancak geriye bakıldığında buna gerek olmayabilir çünkü IList <T>, IEnumerable <T> uygular.
Jon Limjap

69

Çerçeve tasarım yönergeleri , arayan tarafından değiştirilebilen bir koleksiyon veya salt okunur koleksiyonlar için ReadOnlyCollection döndürmeniz gerektiğinde Collection sınıfını kullanmanızı önerir .

Bu bir basit tercih edilir nedeni IListolduğunu IListonun sadece ya da değil okursanız arayan bilgilendirmek etmez.

IEnumerable<T>Bunun yerine bir döndürürseniz , arayanın gerçekleştirmesi için bazı işlemler biraz daha yanıltıcı olabilir. Ayrıca arayan kişiye, isteyebileceğiniz veya istemeyebileceğiniz bir şeyi, koleksiyonu değiştirme esnekliği de vermeyeceksiniz.

LINQ'nun birkaç numara içerdiğini ve belirli çağrıları gerçekleştirildikleri türe göre optimize edeceğini unutmayın. Bu nedenle, örneğin, bir gerçekleştirirseniz Countve temel alınan koleksiyon bir Liste ise, tüm öğelerin üzerinden YÜRÜMEZ.

Kişisel olarak, bir ORM için muhtemelen Collection<T>dönüş değerim olarak bağlı kalırım .


14
Koleksiyon için kılavuz Dos ve DONTs daha ayrıntılı bir listesini içerir.
Chaquotay

27

Genel olarak, en genel olanı talep etmeli ve yapabileceğiniz en spesifik şeyi iade etmelisiniz. Dolayısıyla, bir parametre alan bir yönteminiz varsa ve yalnızca IEnumerable'da mevcut olana gerçekten ihtiyacınız varsa, bu sizin parametre türünüz olmalıdır. Yönteminiz bir IList veya bir IEnumerable döndürebiliyorsa, IList döndürmeyi tercih edin. Bu, en geniş tüketici yelpazesi tarafından kullanılabilmesini sağlar.

İhtiyaç duyduğunuz şeyde gevşek olun ve sağladığınız şeyde açık olun.


1
Tam tersi sonuca vardım: belirli türleri kabul etmeli ve genel türleri döndürmeli. Bununla birlikte, daha geniş olarak uygulanabilir yöntemlerin daha kısıtlı yöntemlerden daha iyi olduğu konusunda haklı olabilirsiniz. Bunu daha çok düşünmem gerekecek.
Dave Cousineau

4
Genel türleri girdi olarak kabul etmenin gerekçesi, bir bileşenden mümkün olduğunca çok yeniden kullanım elde etmek için mümkün olan en geniş girdi aralığıyla çalışmanıza izin vermesidir. Öte yandan, ne tür bir nesneye sahip olduğunuzu zaten tam olarak bildiğiniz için, onu maskelemenin pek bir anlamı yoktur.
Mel

1
Sanırım daha genel parametrelere sahip olmayı kabul ediyorum, ancak daha az genel bir şeyi iade etmek için gerekçeniz nedir?
Dave Cousineau

7
Tamam, bunu başka bir şekilde deneyeyim. Bilgileri neden çöpe attınız? Sonucun yalnızca IEnumerable <T> olmasını önemsiyorsanız, bunun bir IList <T> olduğunu bilmek size herhangi bir şekilde zarar verir mi? Hayır, öyle değil. Bazı durumlarda gereksiz bilgi olabilir, ancak size zarar vermez. Şimdi fayda için. Bir Liste veya IList döndürürseniz, koleksiyonun zaten alınmış olduğunu hemen söyleyebilirim, bu bir IEnumerable ile bilemeyeceğim bir şey. Bu yararlı bilgi olabilir veya olmayabilir, ancak bir kez daha, bilgileri neden çöpe atıyorsunuz? Bir şey hakkında fazladan bilgi biliyorsanız, iletin.
Mel

Geriye dönüp baktığımda, kimsenin beni bu konuda aramamasına şaşırdım. Bir IEnumerable Zaten alındı. Bununla birlikte, bir IQueryable, numaralandırılmamış bir durumda döndürülebilir. Yani belki daha iyi bir örnek, IQueryable ve IEnumerable'ı döndürmek olacaktır. Daha spesifik IQueryable'ın döndürülmesi daha fazla bileşime izin verirken, IEnumerable'ın döndürülmesi anında numaralandırmayı zorlar ve yöntemin oluşturulabilirliğini azaltır.
Mel

23

Bu bağlıdır...

En az türetilmiş türü ( IEnumerable) döndürmek, altta yatan uygulamayı yolun aşağısında değiştirmek için size en fazla alan bırakır.

Daha türetilmiş bir tür ( IList) döndürmek, API'nizin kullanıcılarına sonuç üzerinde daha fazla işlem sağlar.

Her zaman, kullanıcılarınızın ihtiyaç duyacağı tüm işlemlere sahip en az türetilmiş türü döndürmenizi öneririm ... Bu nedenle, temel olarak, ilk olarak, tanımladığınız API bağlamında sonuç üzerindeki hangi işlemlerin anlamlı olduğunu anlamanız gerekir.


diğer yöntemler için de geçerli olduğu için güzel genel yanıt.
user420667

1
Bu cevabın çok eski olduğunu biliyorum ... ama dokümantasyonla çelişiyor gibi görünüyor: "Ne IDictionary <TKey, TValue> arayüzü ne de IList <T> arayüzü gerekli koleksiyonun gereksinimlerini karşılamıyorsa, yeni koleksiyon sınıfını buradan türetin Daha fazla esneklik için ICollection <T> arayüzü "Bu, daha türetilmiş türün tercih edilmesi gerektiği anlamına geliyor gibi görünüyor. ( msdn.microsoft.com/en-us/library/92t2ye13(v=vs.110).aspx ) \
DeborahK

14

Dikkate alınması gereken bir nokta , kendi yönteminizi oluşturmak için ertelenmiş bir yürütme LINQ deyimi kullanıyorsanız , yönteminizden dönmeden önce IEnumerable<T>arama yapmak .ToList(), öğelerinizin iki kez yinelenebileceği anlamına gelir - biri Listeyi oluşturmak için ve biri de arayan dönüş değerinizi filtreler veya dönüştürür. Pratik olduğunda, gerekene kadar LINQ-to-Objects sonuçlarını somut bir Listeye veya Sözlüğe dönüştürmekten kaçınmayı seviyorum. Arayanın bir Listeye ihtiyacı varsa, bu tek bir kolay yöntem aramasıdır - onlar için bu kararı vermeme gerek yok ve bu, arayanın sadece bir foreach yaptığı durumlarda kodumu biraz daha verimli hale getirir.


@Joel Mueller, yine de genellikle ToList () 'i çağırırdım. Genelde IQueryable'ı projelerimin geri kalanına göstermekten hoşlanmıyorum.
KingNestor

4
IQueryable'ın genellikle resme girmediği LINQ-to-Objects'e atıfta bulunuyordum. Bir veritabanı söz konusu olduğunda, ToList () daha gerekli hale gelir, çünkü aksi takdirde yinelemeden önce bağlantınızı kapatma riskiniz vardır, bu da pek iyi çalışmaz. Ancak, bu bir sorun olmadığında, IQueryable'ı gizlemek istediğinizde fazladan bir yinelemeye zorlamadan IQueryable'ı bir IEnumerable olarak ortaya çıkarmak oldukça kolaydır.
Joel Mueller

9

List<T>çağıran kodu, döndürülen nesneyi değiştirme ve dizine göre erişim gibi birçok özellik sunar. Öyleyse soru şu şekildedir: uygulamanızın özel kullanım durumunda, bu tür kullanımları (muhtemelen yeni oluşturulmuş bir koleksiyon geri göndererek!), Arayanın rahatlığı için desteklemek İSTER MİSİNİZ - yoksa basit durum için hız ister misiniz? Arayan kişinin ihtiyacı koleksiyon boyunca döngü içinde olmak ve bunun yanlış bir şekilde değiştirilmesine neden olacağından korkmadan gerçek bir temel koleksiyona bir referansı güvenli bir şekilde iade edebilirsiniz.

Bu soruyu yalnızca siz cevaplayabilirsiniz ve yalnızca arayanlarınızın geri dönüş değeri ile ne yapmak isteyeceğini ve burada performansın ne kadar önemli olduğunu (koleksiyonlarınız ne kadar büyük kopyalarsınız, bunun bir darboğaz olma olasılığı ne kadar? vb).


"Bir referansı yanlış bir şekilde değiştireceğinden korkmadan gerçek bir temel koleksiyona güvenli bir şekilde döndürün" - IEnumerable <T> döndürseniz bile, onu bir Listeye <T> geri çevirip değiştiremezler mi?
Kobi

Her IEnumarable <T> aynı zamanda bir List <T> değildir. Döndürülen nesne List <T> 'den miras alan veya IList <T> uygulayan bir türde değilse, bu bir InvalidCastException ile sonuçlanır.
lowglider

2
List <T>, sizi belirli bir uygulamaya kilitlemekte sorun yaşıyor Collection <T> veya ReadOnlyCollection <T> tercih ediliyor
Sam Saffron

4

Bence ikisini de kullanabilirsin, ama her birinin bir faydası var. Temelde öyledir List, IEnumerableancak sayma işlevine sahipsiniz, öğe ekleyin, öğeyi kaldırın

IEnumerable, öğeleri saymak için verimli değil

Koleksiyonun salt okunur olması amaçlanıyorsa veya koleksiyonun modifikasyonu tarafından kontrol ediliyorsa, Parentbir IListjust for döndürmek Countiyi bir fikir değildir.

Linq'te, CLR'nin içinde temeldeki türün olup olmadığına kısayol olacağı bir Count()genişletme yöntemi vardır , bu nedenle performans farkı ihmal edilebilir.IEnumerable<T>.CountIList

Genel olarak, mümkün olduğunda IEnumerable'ı iade etmenin daha iyi bir uygulama olduğunu düşünüyorum (görüş), eğer eklemeler yapmanız gerekiyorsa, daha sonra bu yöntemleri ana sınıfa ekleyin, aksi takdirde tüketici, Model içindeki koleksiyonu yönetiyor ve bu da ilkeleri manufacturer.Models.Add(model)ihlal ediyor , örn . demeter. Elbette bunlar sadece yönergelerdir ve zor ve hızlı kurallar değildir, ancak uygulanabilirliği tam olarak kavrayana kadar, körü körüne takip etmek, hiç takip etmekten iyidir.

public interface IManufacturer 
{
     IEnumerable<Model> Models {get;}
     void AddModel(Model model);
}

(Not: nNHibernate kullanıyorsanız, farklı erişimciler kullanarak özel IList ile eşlemeniz gerekebilir.)


2

Girdi parametreleri yerine dönüş değerlerinden bahsederken bu o kadar basit değildir. Bir girdi parametresi olduğunda, tam olarak ne yapmanız gerektiğini bilirsiniz. Dolayısıyla, koleksiyon üzerinde yineleme yapabilmeniz gerekiyorsa, bir IEnumberable alırsınız, ancak eklemeniz veya kaldırmanız gerekirse, bir IList alırsınız.

Bir dönüş değeri durumunda, daha zordur. Arayanınız ne bekliyor? Bir IEnumerable döndürürseniz, o zaman bundan bir IList çıkarabileceğini önceden bilemeyecektir. Ancak, bir IList döndürürseniz, üzerinde yineleme yapabileceğini bilecektir. Bu nedenle, arayan kişinin verilerle ne yapacağını hesaba katmalısınız. Arayanınızın ihtiyaç duyduğu / beklediği işlevsellik, neyin iade edileceğine karar verirken neyin yönetmesi gerektiğidir.


0

hepsinin söylediği gibi, katmanı çağırırken Add / Remove functioanlity istemiyorsanız, IEnumerable'a oy vereceğim, çünkü tasarım açısından sevdiğim gibi sadece yineleme ve temel işlevsellik sağlıyor. Geri dönen IList oylarım her zaman yeniden öneriyor ama esas olarak ne sevip ne sevmiyorsunuz. performans açısından daha çok aynı olduklarını düşünüyorum.


0

Harici kodunuzda saymazsanız, IEnumerable'ı döndürmek her zaman daha iyidir, çünkü daha sonra uygulamanızı değiştirebilir (harici kod etkisi olmadan), örneğin yineleyici mantığı elde etmek ve bellek kaynaklarını korumak için (bu arada çok iyi dil özelliği) ).

Ancak, öğe sayısına ihtiyacınız varsa, IEnumerable ile IList - ICollection arasında başka bir katman olduğunu unutmayın .


0

Şimdiye kadar kimsenin önermediğini görünce burada biraz uzakta olabilirim, ama neden bir dönmüyorsun (I)Collection<T>?

Hatırladığım kadarıyla, uygulamayı soyutladığı Collection<T>için tercih edilen dönüş türü oldu List<T>. Hepsi uyguluyor IEnumerable, ama bu bana iş için biraz fazla düşük seviyede geliyor.


0

Bence ikisini de kullanabilirsin, ama her birinin bir faydası var. Temelde öyledir List, IEnumerableancak sayma işlevine sahipsiniz, Öğe ekle, öğe kaldır

IEnumerable öğeleri saymak veya koleksiyondaki belirli bir öğeyi almak için verimli değildir.

List belirli öğeleri bulmak, öğeleri eklemek veya kaldırmak için ideal olan bir koleksiyondur.

Genelde Listmümkün olduğunca kullanmaya çalışıyorum çünkü bu bana daha fazla esneklik sağlıyor.

Kullanım List<FooBar> getRecentItems() yerine IList<FooBar> GetRecentItems()


0

Bence genel kural, gereksiz işleri yapmaktan kaçınmak ve arayan kişiye daha fazla seçenek vermek için geri dönmek için daha spesifik sınıfı kullanmaktır.

Bununla birlikte, yazdığınız kodu önünüzde düşünmenin, bir sonraki kişinin yazacağı koddan daha önemli olduğunu düşünüyorum (mantık çerçevesinde.) Bunun nedeni, zaten var olan kod hakkında varsayımlarda bulunabilmenizdir.

Bir arabirimde IEnumerable'dan bir koleksiyona UP hareketinin işe yarayacağını, bir koleksiyondan IEnumerable'a geçişin mevcut kodu bozacağını unutmayın.

Bu görüşler çelişkili görünüyorsa, bunun nedeni kararın öznel olmasıdır.


0

TL; DR; - özet

  • Şirket içi yazılım geliştiriyorsanız List, koleksiyonlar durumunda bile dönüş değerleri için belirli türü (Like ) ve girdi parametreleri için en genel türü kullanın.
  • Bir yöntem yeniden dağıtılabilir kitaplığın genel API'sinin bir parçasıysa, hem dönüş değerlerini hem de girdi parametrelerini tanıtmak için somut koleksiyon türleri yerine arabirimler kullanın.
  • Bir yöntem salt okunur bir koleksiyon döndürürse, bunu dönüş değeri türü olarak IReadOnlyListveya kullanarak gösterin IReadOnlyCollection.

Daha

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.