web-dev-qa-db-fra.com

Est-ce une bonne pratique de créer un ClassCollection d'une autre classe?

Disons que j'ai une classe Car:

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

Disons que nous faisons un système sur un parking, je vais utiliser beaucoup de la classe Car, donc nous faisons une classe CarCollection, elle peut avoir quelques méthodes supplémentaires comme FindCarByModel:

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

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

Si je crée une classe ParkingLot, quelle est la meilleure pratique?

Option # 1:

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

Option # 2:

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

Est-ce même une bonne pratique de créer un ClassCollection d'un autre Class?

35
Luis

Avant les génériques dans .NET, il était courant de créer des collections "typées" pour que vous ayez class CarCollection Etc. pour chaque type à regrouper. Dans .NET 2.0 avec l'introduction des génériques, une nouvelle classe List<T> A été introduite qui vous évite d'avoir à créer CarCollection etc car vous pouvez créer List<Car>.

La plupart du temps, vous constaterez que List<T> Est suffisant pour vos besoins, mais il peut y avoir des moments où vous souhaitez avoir un comportement spécifique dans votre collection, si vous pensez que c'est le cas, vous en avez deux d'options:

  • Créez une classe qui encapsule List<T> Par exemple public class CarCollection { private List<Car> cars = new List<Car>(); public void Add(Car car) { this.cars.Add(car); }}
  • Créez une collection personnalisée public class CarCollection : CollectionBase<Car> {}

Si vous optez pour l'approche d'encapsulation, vous devez au minimum exposer l'énumérateur afin de le déclarer comme suit:

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

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

Sans cela, vous ne pouvez pas faire un foreach sur la collection.

Voici quelques raisons pour lesquelles vous souhaiterez peut-être créer une collection personnalisée:

  • Vous ne voulez pas exposer entièrement toutes les méthodes dans IList<T> Ou ICollection<T>
  • Vous souhaitez effectuer des actions supplémentaires lors de l'ajout ou de la suppression d'un élément de la collection

Est-ce une bonne pratique? bien cela dépend de pourquoi vous le faites, si c'est par exemple une des raisons que j'ai énumérées ci-dessus alors oui.

Microsoft le fait assez régulièrement, voici quelques exemples assez récents:

Quant à vos méthodes FindBy, je serais tenté de les mettre dans des méthodes d'extension afin qu'elles puissent être utilisées contre toute collection contenant des voitures:

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

    ...
}

Cela sépare le souci d'interroger la collection de la classe qui stocke les voitures.

40
Trevor Pilley

Non. La création de classes XXXCollection est pratiquement tombée en désuétude avec l'avènement des génériques dans .NET 2.0. En fait, il y a l'extension astucieuse Cast<T>() LINQ que les gens utilisent de nos jours pour extraire des trucs de ces formats personnalisés.

9
Jesse C. Slicer

Il est souvent pratique d'avoir des méthodes orientées domaine pour rechercher/découper des collections, comme dans l'exemple FindByCarModel ci-dessus, mais il n'est pas nécessaire de recourir à la création de classes de collections wrapper. Dans cette situation, je vais maintenant généralement créer un ensemble de méthodes d'extension.

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

Vous ajoutez autant de filtres ou de méthodes utilitaires à cette classe que vous le souhaitez, et vous pouvez les utiliser partout où vous avez IEnumerable<Car>, qui comprend tout ICollection<Car>, tableaux de Car, IList<Car> etc.

Étant donné que notre solution de persistance a un fournisseur LINQ, je vais aussi souvent créer des méthodes de filtrage similaires qui fonctionnent et renvoient IQueryable<T>, afin que nous puissions également appliquer ces opérations au référentiel.

L'idiome de .NET (enfin, C #) a beaucoup changé depuis 1.1. La maintenance des classes de collection personnalisées est pénible et vous gagnez peu à hériter de CollectionBase<T> que vous n'obtenez pas avec la solution de méthode d'extension si vous n'avez besoin que de méthodes de filtrage et de sélection spécifiques au domaine.

2
Neil Hewitt

Je pense que la seule raison de créer une classe spéciale pour contenir une collection d'autres éléments devrait être lorsque vous y ajoutez quelque chose de valeur, quelque chose de plus que simplement encapsuler/hériter d'une instance de IList ou d'un autre type de collection.

Par exemple, dans votre cas, ajouter une fonction qui retournerait des sous-listes de voitures garées sur des espaces de lots pairs/irréguliers ... Et même alors ... peut-être seulement si elle est souvent réutilisée, car si elle ne prend qu'une seule ligne avec un Nice LinQ fonctionne et n'est utilisé qu'une seule fois, à quoi ça sert? EMBRASSE !

Maintenant, si vous prévoyez d'offrir beaucoup de méthodes de tri/recherche, alors oui, je pense que cela pourrait être utile parce que c'est là qu'elles devraient appartenir, dans cette classe de collection spéciale. C'est également un bon moyen de "masquer" la complexité de certaines requêtes "find" ou tout ce que vous pourriez faire dans une méthode de tri/recherche.

1
Jalayn