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
?
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:
List<T>
Par exemple public class CarCollection { private List<Car> cars = new List<Car>(); public void Add(Car car) { this.cars.Add(car); }}
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:
IList<T>
Ou ICollection<T>
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.
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.
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.
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.