Başka bir sınıfa ait bir ClassCollection oluşturmak iyi bir uygulama mıdır?


35

Bir Carsınıfım olduğunu söyleyelim:

public class Car
{
    public string Engine { get; set; }
    public string Seat { get; set; }
    public string Tires { get; set; }
}

Diyelim ki bir park yeri hakkında bir sistem yapıyoruz, Carsınıfın çoğunu kullanacağım , bu yüzden bir CarCollectionsınıf yapıyoruz , bunun gibi birkaç ekosistem yöntemi olabilir FindCarByModel:

public class CarCollection
{
    public List<Car> Cars { get; set; }

    public Car FindCarByModel(string model)
    {
        // code here
        return new Car();
    }
}

Bir sınıf yapıyorum ParkingLot, en iyi uygulama nedir?

Seçenek 1:

public class ParkingLot
{
    public List<Car> Cars { get; set; }
    //some other properties
}

Seçenek 2:

public class ParkingLot
{
    public CarCollection Cars { get; set; }
    //some other properties
}

ClassCollectionBaşka bir tane oluşturmak bile iyi bir uygulama Classmı?


Etrafından CarCollectionziyade geçmenin nasıl bir yararı olduğunu düşünüyorsun List<Car>? Özellikle CarCollection'ın destek Listesini genişletmediği, hatta Koleksiyon arayüzünü uygulamadığı göz önüne alındığında (C # 'nın benzer şeylere sahip olduğundan eminim).

<T> listesi zaten IList <T>, ICollection <T>, IList, ICollection, IReadOnlyList <T>, IReadOnlyCollection <T>, IEnumerable <T> ve IEnumerable öğelerini uygular ... Ayrıca, Linq kullanabilirim ...
Luis

Fakat public class CarCollectionIList veya ICollection vb. Uygulamıyor ... böylece bir liste ile iyi olan birşeye geçemezsiniz. Adının bir parçası olduğunu bir koleksiyon olduğunu iddia eder, ancak bu yöntemlerden hiçbirini uygulamaz.

1
6 yaşındaki bir soru olarak, kimsenin DDD'de yaygın bir uygulama olduğunu söylemediğini gördüm. Herhangi bir koleksiyon özel bir koleksiyona alınmalıdır. Örneğin, bir otomobil grubunun değerini hesaplamak istediğinizi varsayalım. Bu mantığı nereye koyardın? bir serviste mi Veya DDD’de üzerinde CarColectionbir TotalTradeValueözellik var. DDD, sistemleri tasarlamanın tek yolu değil, sadece bir seçenek olarak işaret ediyor.
Storm Muller

1
Aynen, bunlar DDD'de önerildiği gibi aslında toplam köklerdir, Tek şey DDD'nin muhtemelen bir araba koleksiyonu olarak adlandırmanızı tavsiye etmeyeceğidir. Aksine, Park Yeri veya Çalışma Alanı vb. Gibi bazı isimler kullanın.
Kod Adı Jack

Yanıtlar:


40

.NET'teki jenerik ürünlerden önce, 'tiplenmiş' koleksiyonlar oluşturmak yaygın bir pratikti, böylece class CarCollectiongruplamak için ihtiyacınız olan her tür için vb. Generics’in tanıtımıyla birlikte .NET 2.0’da, List<T>yarattığınız CarCollectiongibi yaratmanız gibi tasarruf etmenizi sağlayan yeni bir sınıf tanıtıldı List<Car>.

Çoğu zaman bunun List<T>amaçlarınız için yeterli olduğunu göreceksiniz , ancak koleksiyonunuzda belirli davranışlarda bulunmak istediğiniz zamanlar olabilir, bunun böyle olduğuna inanıyorsanız, birkaç seçeneğiniz vardır:

  • List<T>Örneğin kapsülleyen bir sınıf oluşturunpublic class CarCollection { private List<Car> cars = new List<Car>(); public void Add(Car car) { this.cars.Add(car); }}
  • Özel bir koleksiyon oluştur public class CarCollection : CollectionBase<Car> {}

Kapsülleme yaklaşımı için giderseniz, en azından numaralandırıcıyı göstermelisiniz ki böylelikle aşağıdaki gibi beyan etmelisiniz:

public class CarCollection : IEnumerable<Car>
{
    private List<Car> cars = new List<Car>();

    public IEnumerator<Car> GetEnumerator() { return this.cars.GetEnumerator(); }
}

Bunu yapmadan foreach, koleksiyonun üstünden yapamazsın .

Özel bir koleksiyon oluşturmak isteyebileceğiniz bazı nedenler:

  • Tam olarak tüm yöntemleri göstermek istemiyorum IList<T>yaICollection<T>
  • Bir öğeyi koleksiyona eklerken veya kaldırırken ek işlemler yapmak istiyorsunuz.

İyi bir uygulama mı? peki bu neden yaptığınıza bağlıdır, örneğin yukarıda listelediğim sebeplerden biriyse evet.

Microsoft oldukça düzenli olarak yapıyor, işte size oldukça yeni örnekler:

FindByYöntemlerine gelince , onları araba içeren herhangi bir koleksiyona karşı kullanabilmeleri için uzatma yöntemlerine koyarım.

public static class CarLookupQueries
{
    public static Car FindByLicencePlate(this IEnumerable<Car> source, string licencePlate)
    {
        return source.SingleOrDefault(c => c.LicencePlate == licencePlate);
    }

    ...
}

Bu, koleksiyonun sorgulanması endişesini arabaları depolayan sınıftan ayırır.


Bu yaklaşımı izleyerek, ClassCollectionekleme, silme, güncelleme yöntemleri için bile, CarCRUDtüm bu yöntemleri kapsayacak bir yenisini ekleyerek bile reddedebilirdim ...
Luis

@Luis CarCRUD, kullanımını zorlaştırmak için uzantıyı önermeyeceğim , özel sınıf mantığını toplama sınıfına koymanın avantajı, onu atlamanın bir yolu olmamasıdır. Ayrıca, gerçekte Carbildirilen çekirdek derlemede Bul mantığını umursamayabilirsiniz , bu yalnızca bir UI etkinliği olabilir.
Trevor Pilley

MSDN'den bir not gibi: "Yeni geliştirme için CollectionBase sınıfını kullanmanızı önermiyoruz. Bunun yerine, genel Koleksiyon <T> sınıfını kullanmanızı öneririz." - docs.microsoft.com/en-us/dotnet/api/…
Ryan,

9

Hayır. XXXCollectionSınıfların oluşturulması, .NET 2.0'daki jeneriklerin ortaya çıkmasıyla hemen hemen stil dışı kalmıştır. Aslında, günümüzde Cast<T>()insanların bu özel biçimlerden bir şeyler almak için kullandığı şık bir LINQ uzantısı var.


1
Bunun içinde sahip olabileceğimiz özel yöntemlerden ne haber ClassCollection? onları ana noktaya koymak iyi bir uygulama Classmı?
Luis,

3
Bunun "bağlıdır" yazılım geliştirme mantığına girdiğine inanıyorum. Örneğin, FindCarByModelyönteminizden bahsediyorsanız , bu, deponuzda bir yöntem olarak anlam ifade eder, bu sadece bir Carkoleksiyondan biraz daha karmaşıktır .
Jesse C. Dilimleyici

2

Yukarıdaki FindByCarModel örneğinde olduğu gibi koleksiyonları bulmak / dilimlemek için etki alanı odaklı yöntemlere sahip olmak genellikle kullanışlıdır, ancak sarmalayıcı toplama sınıfları oluşturmaya başvurmaya gerek yoktur. Bu durumda şimdi tipik olarak bir dizi uzatma yöntemi oluşturacağım.

public static class CarExtensions
{
    public static IEnumerable<Car> ByModel(this IEnumerable<Car> cars, string model)
    {
        return cars.Where(car => car.Model == model);
    }
}

İstediğiniz bu sınıfa birçok filtre veya kamu yöntemleri gibi ekleyin ve sahip olduğunuz her yerde kullanabilirsiniz IEnumerable<Car>şey içeren, ICollection<Car>ait diziler Car, IList<Car>vb

Kalıcılık çözümümüz bir LINQ sağlayıcısına sahip olduğundan, sıklıkla çalışan ve geri dönen benzer filtre yöntemleri de oluşturacağım IQueryable<T>, böylece bu işlemleri depoya da uygulayabiliriz.

.NET deyimi (peki, C #) 1.1'den beri çok değişti. Özel koleksiyon sınıflarını CollectionBase<T>sürdürmek bir acıdır ve ihtiyacınız olan tek şey etki alanına özgü filtre ve seçici yöntemlerden biriyse , uzatma yöntemi çözümünden alamadığınızdan çok az şey kazanırsınız .


1

Başka öğelerin koleksiyonunu tutmak için özel bir sınıf oluşturmanın tek sebebinin, ona değerli bir şey eklediğinizde, sadece bir kapama örneğinden IListveya başka bir koleksiyon türünden devralmaktan / devralmaktan başka bir şey olması gerektiğini düşünüyorum .

Örneğin, sizin durumunuzda, hatta / düzensiz alanlara park etmiş arabaların alt listelerini döndürecek bir işlev eklemek ... Ve o zaman bile ... belki de sadece tekrar kullanılırsa, çünkü güzel bir LinQ ile sadece bir satır alırsa işlev ve yalnızca bir kez kullanılırsa, amaç ne? KISS !

Şimdi, çok fazla sıralama / bulma yöntemi sunmayı planlıyorsanız, o zaman evet, bunun yararlı olabileceğini düşünüyorum çünkü bu özel koleksiyon sınıfına ait olmaları gereken yer burasıdır. Bu aynı zamanda bazı "bulma" sorgularının karmaşıklığını "gizleme" ya da bir sıralama / bulma yönteminde yapabilecekleriniz için de iyi bir yoldur.


Tho bile, sanırım bu yöntemleri ana Class
Luis

Evet, gerçekten ... yapabilirsiniz
Jalayn

-1

Aşağıdaki seçenekle devam etmeyi tercih ediyorum, böylece koleksiyonunuza yöntem ekleyebilir ve listenin avantajını kullanabilirsiniz.

public class CarCollection:List<Car>
{
    public Car FindCarByModel(string model)
    {
        // code here
        return new Car();
    }
}

ve sonra C # 7.0 gibi kullanabilirsiniz

public class ParkingLot
{
    public CarCollection Cars { get; set; }=new CarCollection();
    //some other properties
}

Ya da gibi kullanabilirsiniz

public class ParkingLot
{
   public ParkingLot()
   {
      //initial set
      Cars =new CarCollection();
   }
    public CarCollection Cars { get; set; }
    //some other properties
}

- @Bryan yorumu sayesinde genel sürüm

   public class MyCollection<T>:List<T> where T:class,new()
    {
        public T FindOrNew(Predicate<T> predicate)
        {
            // code here
            return Find(predicate)?? new T();
        }
       //Other Common methods
     }

ve sonra kullanabilirsiniz

public class ParkingLot
{
    public MyCollection<Car> Cars { get; set; }=new MyCollection<Car>();
    public MyCollection<Motor> Motors{ get; set; }=new MyCollection<Motor>();
    public MyCollection<Bike> Bikes{ get; set; }=new MyCollection<Bike>();
    //some other properties
}

Listeden miras
almayın

@Bryan, soruyu genel toplama için değil, haklısınız. Genel koleksiyon için cevabımı değiştireceğim.
Waleed AK

1
@WaleedAK, hala yaptınız - Listeden miras almayın <T>: stackoverflow.com/questions/21692193/why-not-inherit-from-listt
Bryan Boettcher

@Bryan: Eğer bağlantınızı okursanız Ne zaman kabul edilebilir? <T> Listesi mekanizmasını genişleten bir mekanizma oluştururken. , Öyleyse ek mülk bulunmadığı sürece sorun olmayacak
Waleed AK
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.