Koleksiyon <T> ve Liste <T> arayüzlerinizde ne kullanmalısınız?


162

Kod aşağıdaki gibi görünür:

namespace Test
{
    public interface IMyClass
    {
        List<IMyClass> GetList();
    }

    public class MyClass : IMyClass
    {
        public List<IMyClass> GetList()
        {
            return new List<IMyClass>();
        }
    }
}

Kod Analizi Çalıştırdığımda aşağıdaki öneriyi alırım.

Uyarı 3 CA1002: Microsoft.Design: Collection, ReadOnlyCollection veya KeyedCollection kullanmak için 'IMyClass.GetList ()' içindeki 'List' öğesini değiştirin

Bunu nasıl düzeltmeliyim ve burada iyi uygulama nedir?

Yanıtlar:


234

Sorunun neden olmasa da "neden" bölümünü yanıtlamak için List<T>, nedenleri geleceğe hazırlayan ve API basitliğidir.

Gelecek geçirmezlik

List<T>alt sınıflandırma ile kolayca genişletilebilecek şekilde tasarlanmamıştır; dahili uygulamalar için hızlı olacak şekilde tasarlanmıştır. Üzerindeki yöntemlerin sanal olmadığını fark edeceksiniz ve bu yüzden geçersiz kılınamaz ve Add/ Insert/ Removeişlemlerinde kanca yoktur .

Bu, ileride koleksiyonun davranışını değiştirmeniz gerektiğinde (örneğin, insanların eklemeye çalıştığı boş nesneleri reddetmek veya bu durum gerçekleştiğinde sınıf durumunuzu güncellemek gibi ek işler gerçekleştirmek için), türü değiştirmeniz gerektiği anlamına gelir. toplama sınıfına dönebilirsiniz alt sınıf, ki bu bir kırılma arayüzü değişikliği olacak (tabii ki null izin vermemek gibi şeylerin anlamlarını değiştirmek de bir arayüz değişikliği olabilir, ama iç sınıf durumunuzu güncelleme gibi şeyler olmayacaktır).

Yani ya kolayca gibi sınıflandırma edilebilir bir sınıf döndürerek Collection<T>gibi bir arayüz veya IList<T>, ICollection<T>ya IEnumerable<T>da hala iade edilebilir çünkü tüketicilerin kodunu bozmadan, kendi ihtiyaçlarını karşılamak için farklı bir koleksiyon türü olmak için iç uygulanmasını değiştirebilir bekliyorlar.

API Basitliği

List<T>gibi yararlı bir operasyon çok içerir BinarySearch, Sortve böyle devam eder. Bununla birlikte, bu ortaya koyduğunuz bir koleksiyonsa, tüketicilerin değil, listenin anlambilimini kontrol etmeniz muhtemeldir. Sınıfınız dahili olarak bu işlemlere ihtiyaç duysa da, sınıfınızın tüketicilerinin onları aramak istemesi (hatta hatta yapması) pek olası değildir.

Bu nedenle, daha basit bir toplama sınıfı veya arayüzü sunarak API'nizin kullanıcılarının gördüğü üye sayısını azaltır ve kullanımlarını kolaylaştırırsınız.


1
Bu cevap öldü. Konuyla ilgili bir başka iyi okuma burada bulunabilir: blogs.msdn.com/fxcop/archive/2006/04/27/…
senfo

7
İlk puanlarınızı görüyorum, ancak API basitlik bölümünüze katılıp katılmadığımı bilmiyorum.
Boris Callens

stackoverflow.com/a/398988/2632991 İşte Koleksiyonlar ve Listeler arasındaki farkın ne olduğu hakkında da iyi bir gönderi.
El Mac

2
blogs.msdn.com/fxcop/archive/2006/04/27/… -> Öldü. Bunun için daha yeni bir bilgimiz var mı?
Machado


50

Şahsen bunu somut bir koleksiyondan ziyade bir arayüz iade ettiğini beyan ederim. Listeye gerçekten erişmek istiyorsanız, tuşunu kullanın IList<T>. Aksi takdirde, düşünün ICollection<T>ve IEnumerable<T>.


1
IList ICollection arayüzünü genişletir mi?
bovium

8
@Jon: Bunun eski olduğunu biliyorum, ama Krzysztof'un blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx adresinde neler söylediği hakkında yorum yapabilir misiniz ? Özellikle yorumu, We recommend using Collection<T>, ReadOnlyCollection<T>, or KeyedCollection<TKey,TItem> for outputs and properties and interfaces IEnumerable<T>, ICollection<T>, IList<T> for inputs. CA1002, Krzysztof'un yorumlarıyla birlikte gidiyor gibi görünüyor. Arayüz yerine neden somut bir koleksiyonun önerileceğini ve girişler / çıkışlar arasındaki ayrımı neden hayal edemiyorum.
Nelson Rothermel

3
@Nelson: Size istediğiniz nadir gerektiren bir iletmenin listede geçmesine arayanları, ancak mantıklı olduğunu dönmek onlar kesinlikle değişmez biliyorum ki birini. Ama diğer koleksiyonlar hakkında emin değilim. Daha fazla ayrıntıya sahip olmak güzel olurdu.
Jon Skeet

2
Belirli bir durum için değil. Açıkçası genel ReadOnlyCollection<T>olarak bir girdi için mantıklı olmazdı. Benzer şekilde, IList<T>bir girdinin söylediği gibi, "Sort () veya bir IList'in sahip olduğu başka bir üyeye ihtiyacım var", bu da bir çıktı için mantıklı değil. Ama demek istediğim, neden ICollection<T>bir girdi ve Collection<T>çıktı olarak önerilecekti. Neden değil kullanmak ICollection<T>size önerildiği gibi de çıkış olarak?
Nelson Rothermel

9
Bunun belirsizliğe bağlı olduğunu düşünüyorum. Collection<T>ve ReadOnlyCollection<T>her ikisi de türetilmiştir ICollection<T>(yani hayır IReadOnlyCollection<T>). Arabirimi döndürürseniz, hangisinin olduğu ve değiştirilip değiştirilemeyeceği açık değildir. Her neyse, girişiniz için teşekkürler. Bu benim için iyi bir akıl sağlığı kontrolü oldu.
Nelson Rothermel

3

Kimsenin "neden" kısmına henüz cevap vermediğini sanmıyorum ... işte gidiyor. A Collection<T>yerine "neden" siz "kullanmanızın List<T>nedeni, a'yı ortaya çıkarırsanız List<T>, nesnenize erişen herkesin listedeki öğeleri değiştirebilmesidir. Oysa Collection<T>kendi "Ekle", "Kaldır" vb. Yöntemlerinizi yaptığınızı belirtmesi gerekir.

Muhtemelen bunun için endişelenmenize gerek yok, çünkü muhtemelen arayüzü sadece kendiniz (veya belki de birkaç meslektaşınız için) kodluyorsunuz. İşte mantıklı olabilecek başka bir örnek.

Herkese açık bir diziniz varsa, ör:

public int[] MyIntegers { get; }

Hiç kimsenin değerlerle uğraşamayacağı bir "get" erişimcisi olduğu için düşünürsünüz, ama bu doğru değil. Herkes içindeki değerleri şu şekilde değiştirebilir:

someObject.MyIngegers[3] = 12345;

Şahsen, List<T>çoğu durumda kullanırım. Ancak rastgele geliştiricilere vereceğiniz bir sınıf kütüphanesi tasarlıyorsanız ve nesnelerin durumuna güvenmeniz gerekiyorsa ... o zaman kendi Koleksiyonunuzu yapmak ve oradan kilitlemek istersiniz: )


"<T> Listesini istemci koduna döndürürseniz, istemci kodu koleksiyonu değiştirdiğinde hiçbir zaman bildirim alamazsınız." - FX COP ... Ayrıca "Bkz msdn.microsoft.com/en-us/library/0fss9skc.aspx vay, bütün :) sonra üsten değilim görünüyor ..."
Timothy Khouri

Vay canına - yıllar sonra yukarıdaki yoruma yapıştırdığım bağlantının sonunda bir alıntı olduğunu gördüm, bu yüzden işe yaramadı ... msdn.microsoft.com/en-us/library/0fss9skc.aspx
Timothy Khouri

2

Bu, çoğunlukla List nesnesini doğrudan manipüle edilmek yerine kendi uygulamalarınızı soyutlamakla ilgilidir.

Diğer nesnelerin (veya kişilerin) doğrudan nesnelerinizin durumunu değiştirmesine izin vermek iyi bir uygulama değildir. Mülkiyet alıcılarını / düzenleyicilerini düşünün.

Koleksiyon -> Normal koleksiyon için
ReadOnlyCollection -> Değiştirilmemesi gereken koleksiyonlar için
KeyedCollection -> Bunun yerine sözlük istediğinizde.

Nasıl düzeltileceği, sınıfınızın ne yapmasını istediğinize ve GetList () yönteminin amacına bağlıdır. Detaylandırabilir misin?


1
Ancak <T> ve KeyedCollection <,> Koleksiyonu da salt okunur değildir.
nawfal

@nawfal Ve bunun nerede olduğunu söyledim?
chakrit

1
Sen açıkça belirtin diğer nesneleri (veya kişi) da nesnelerin doğrudan durumunu değiştirmesine olanak ve liste 3 toplama türleri. Engelleme ReadOnlyCollectiondiğer ikisini ona uyun etmez.
nawfal

Bu, "iyi uygulama" anlamına gelir. Uygun içerik lütfen. Aşağıdaki liste, OP'nin uyarıyı anlamak istediğinden, bu tür türlerin temel gereksinimlerini belirtmektedir. Daha sonra GetList()ona daha doğru bir şekilde yardım edebilme amacını sormaya devam ediyorum .
chakrit

1
Tamam, o kısmı anlıyorum. Ama yine de akıl yürütme kısmı bana göre sağlam değil. Collection<T>Dahili uygulamanın soyutlanmasına yardımcı olduğunu ve dahili listenin doğrudan değiştirilmesini önlediğini söylüyorsunuz . Nasıl? Collection<T>yalnızca aynı örnek üzerinde çalışan bir sarıcıdır. Kalıtım amaçlı dinamik bir koleksiyon, başka bir şey yok (Greg'in cevabı burada daha alakalı).
nawfal

1

Bu tür bir durumda genellikle gerekli olan en az miktarda uygulamayı ortaya çıkarmaya çalışırım. Tüketicilerin aslında bir liste kullandığınızı bilmeleri gerekmiyorsa, bir liste döndürmeniz gerekmez. Microsoft'un bir Koleksiyon önerdiği gibi geri dönerek, sınıfınızın tüketicilerinden bir liste kullandığınız gerçeğini gizler ve bunları dahili bir değişikliğe karşı izole edersiniz.


1

Eklenmesi gereken bir şey, bu sorulduğundan beri uzun zaman geçti.

Liste türünüz List<T>yerine türetildiğinde, Collection<T>uygulanmış korumalı sanal yöntemleri Collection<T>uygulayamazsınız. Bunun anlamı türetilmiş türün listede herhangi bir değişiklik yapılması durumunda yanıt veremeyeceğidir. Bunun nedeni List<T>, öğe eklediğinizde veya kaldırdığınızda farkında olduğunuzu varsayar. Bildirimlere yanıt verebilmek bir ek yüktür ve bu nedenle List<T>önermez.

Harici kodun koleksiyonunuza erişimi olduğu durumlarda, bir öğenin ne zaman ekleneceğini veya kaldırılacağını kontrol edemeyebilirsiniz. Bu nedenle Collection<T>, listenizin ne zaman değiştirildiğini bilmek için bir yol sağlar.


0

Gibi bir şey döndürürken herhangi bir sorun görmüyorum

this.InternalData.Filter(crteria).ToList();

Dahili verilerin bağlantısı kesilmiş bir kopyasını veya bir veri sorgusunun ayrılmış sonucunu döndürdüysem - List<TItem>Uygulama ayrıntılarını göstermeden güvenli bir şekilde geri dönebilirim ve iade edilen verileri uygun bir şekilde kullanmaya izin verebilirim.

Ancak bu, ne tür bir tüketici beklediğime bağlıdır - bu veri ızgarası gibi bir şeyse, çoğu durumda yine de IEnumerable<TItem> öğelerin kopyalanan listesi olacak geri dönmeyi tercih ederim :)


-1

Koleksiyon sınıfı, uygulama ayrıntılarını ve diğer özelliklerini gizlemek için diğer koleksiyonların etrafındaki bir sarmalayıcı sınıftır. Bunun, nesne yönelimli dillerde kodlama desenini gizleyen özellik ile ilgisi olduğunu düşünüyorum.

Bu konuda endişelenmemelisiniz, ancak gerçekten kod analiz aracını memnun etmek istiyorsanız, sadece aşağıdakileri yapın:

//using System.Collections.ObjectModel;

Collection<MyClass> myCollection = new Collection<MyClass>(myList);

1
Üzgünüm, bu bir yazım hatasıydı. <MyClass> Koleksiyonuna uyuyorum. Gerçekten C # 4 ve genel kovaryans btw ellerimi almak için sabırsızlanıyorum!
Tamas Czinege

Kaydırma, Collection<T>ne kendisini ne de altta yatan koleksiyonu anladığım kadar korur.
nawfal

Bu kod parçası "kod analiz aracını memnun etmek" tir. @TamasCzinege'in, Collection<T>kullanımın hemen altındaki koleksiyonunuzu koruyacağını söylediğini sanmıyorum .
chakrit
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.