创建另一个类的ClassCollection是一个好习惯吗?


35

可以说我有一Car堂课:

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

可以说,我们正在建立一个有关停车场的系统,我将使用很多Car类,因此我们建立了一个CarCollection类,它可能有一些类似的方法FindCarByModel

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

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

如果要上课ParkingLot,什么是最佳做法?

选项1:

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

选项2:

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

创建ClassCollection另一个甚至是一个好习惯Class吗?


您认为传球CarCollection而不是传球有什么好处List<Car>?特别是考虑到CarCollection不会扩展后备List类,甚至不会实现Collection接口(我确信C#具有类似的功能)。

List <T>已经实现了IList <T>,ICollection <T>,IList,ICollection,IReadOnlyList <T>,IReadOnlyCollection <T>,IEnumerable <T>和IEnumerable...。此外,我可以使用Linq ...
Luis

但是public class CarCollection不实现IList或ICollection等。因此,您不能将其传递给列表可以的东西。作为其名称的一部分,它声称它是一个集合,但未实现任何这些方法。

1
作为一个已有6年历史的问题,我认为没有人提到这是DDD中的常见做法。任何集合都应抽象为自定义集合。例如,说您想计算一组汽车的价值。您会将逻辑放在哪里?在服务中?或者在DDD中,您将拥有一个CarColection带有TotalTradeValue属性的。DDD并不是设计系统的唯一方法,只是指出它是一种选择。
Storm Muller

1
确实,这些东西实际上是DDD中建议的集合根,只有DDD可能不建议您将其称为“汽车收藏”。而是使用像停车场,或工作区等一些名字
代码名称杰克

Answers:


40

在.NET中使用泛型之前,通常的做法是创建“类型化”集合,因此class CarCollection对于需要分组的每种类型,您都会拥有etc。在.NET 2.0中,随着泛型的引入,引入了一个新类List<T>,它使您不必CarCollection像创建其他类一样创建List<Car>

在大多数情况下,您会发现List<T>满足您的目的就足够了,但是有时您可能希望在集合中具有特定的行为,如果您认为是这种情况,则有两种选择:

  • 创建一个封装List<T>例如的类public class CarCollection { private List<Car> cars = new List<Car>(); public void Add(Car car) { this.cars.Add(car); }}
  • 创建一个自定义集合 public class CarCollection : CollectionBase<Car> {}

如果您采用封装方法,则至少应公开枚举器,以便对其进行如下声明:

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

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

否则,您将无法完成foreach整个集合。

您可能要创建自定义集合的一些原因是:

  • 您不想完全公开IList<T>或中的所有方法ICollection<T>
  • 您想要在添加或删除集合中的项目时执行其他操作

这是好习惯吗?好吧,这取决于您为什么这样做,例如,如果这是我在上面列出的原因之一,那么可以。

Microsoft定期执行此操作,以下是一些最近的示例:

至于您的FindBy方法,我很想将它们放在扩展方法中,以便可以将它们用于包含汽车的任何集合中:

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

    ...
}

这将查询集合的注意与存储汽车的类分开了。


按照这种方法,我什至ClassCollection可以通过添加CarCRUD将封装所有这些方法的新方法来消除添加,删除,更新方法的偶数……
Luis

@Luis我不建议CarCRUD扩展,因为强制使用它会很困难,将自定义类逻辑放入集合类的好处是无法绕开它。此外,您可能实际上并不关心Car声明了etc 的核心程序集中的Find逻辑,这可能是仅UI的活动。
Trevor Pilley

正如MSDN的注释:“我们不建议您将CollectionBase类用于新开发。相反,我们建议您使用通用Collection <T>类。” - docs.microsoft.com/en-us/dotnet/api/...
瑞恩

9

XXXCollection不会。随着.NET 2.0中泛型的出现,类的创建几乎已经过时了。实际上,如今Cast<T>()人们使用了一个漂亮的LINQ扩展来从那些自定义格式中获取内容。


1
那我们里面可以有什么自定义方法ClassCollection呢?将它们放在主体上是一个好习惯Class吗?
2013年

3
我相信这属于“取决于”的软件开发原则。例如,如果您谈论的是FindCarByModel方法,那么这是作为存储库中的方法有意义的,它比Car集合要复杂得多。
Jesse C. Slicer

2

如上面的FindByCarModel示例中那样,具有面向域的方法来查找/切片集合通常很方便,但是无需诉诸于创建包装器集合类。在这种情况下,我现在通常将创建一组扩展方法。

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

您添加尽可能多的过滤器或实用方法的那类,只要你喜欢,你可以在任何地方使用他们,你有IEnumerable<Car>,其中包括什么ICollection<Car>,数组CarIList<Car>等等。

由于我们的持久性解决方案具有LINQ提供程序,因此我还将经常创建类似的过滤器方法,IQueryable<T>这些方法可在上操作并返回,因此我们也可以将这些操作应用于存储库。

自1.1版以来,.NET的习惯用法(C#)已经发生了很大变化。维护自定义集合类是一件很痛苦的CollectionBase<T>事情,如果您只需要特定于域的过滤器和选择器方法,那么继承继承就不会带来任何好处,而扩展方法解决方案是您无法获得的。


1

我认为,创建一个特殊类来保存其他项目的集合的唯一原因应该是当您向其中添加有价值的东西时,而不仅仅是封装/继承一个实例IList或另一种类型的集合。

例如,在您的情况下,添加了一个函数,该函数将返回停在偶数/不均匀地段上的汽车的子列表...即使这样,也可能仅在经常重复使用时使用,因为如果只用一行就可以了一个很好的LinQ功能并且仅使用一次,这有什么意义?

现在,如果您打算提供许多排序/查找方法,那么是的,我认为这可能很有用,因为这是它们应该属于该特殊集合类的位置。这也是“隐藏”某些“查找”查询的复杂性或在排序/查找方法中可以执行的任何操作的好方法。


甚至,我想我也可以将这些方法包括在内Class
Luis

是的,确实...您可以
Jalayn

-1

我更喜欢使用以下选项,因此您可以将方法添加到集合中并利用list的优势。

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

然后可以像C#7.0一样使用它

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

或者你可以像这样使用它

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

-通用版本感谢@Bryan评论

   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
     }

然后你可以使用它

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
}

不要从List <T>继承
Bryan Boettcher

@Bryan,您说对了,这不是通用集合的问题。我将修改通用收集的答案。
Waleed AK

1
@WaleedAK您仍然做到了-不要继承List <T>: stackoverflow.com/questions/21692193/why-not-inherit-from-listt
Bryan Boettcher

@Bryan:如果您阅读了链接,何时可以接受?在构建扩展List <T>机制的机制时。,所以只要没有其他属性就可以了
Waleed AK
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.