Question

Avant les génériques C#, tout le monde codait des collections pour ses objets métier en créant une base de collection implémentant IEnumerable.

C'EST À DIRE:

public class CollectionBase : IEnumerable

et en dériveraient ensuite leurs collections Business Object.

public class BusinessObjectCollection : CollectionBase

Maintenant, avec la classe de liste générique, est-ce que quelqu'un l'utilise à la place ?J'ai constaté que j'utilise un compromis entre les deux techniques :

public class BusinessObjectCollection : List<BusinessObject>

Je fais cela parce que j'aime avoir des noms fortement typés au lieu de simplement transmettre des listes.

Qu'est-ce que ton approche?

Était-ce utile?

La solution

Je suis généralement du côté de l'utilisation directe d'une liste, à moins que, pour une raison quelconque, je doive encapsuler la structure des données et fournir un sous-ensemble limité de ses fonctionnalités.C'est principalement parce que si je n'ai pas de besoin spécifique d'encapsulation, le faire n'est qu'une perte de temps.

Cependant, avec la fonctionnalité d'initialisation globale de C# 3.0, il existe de nouvelles situations dans lesquelles je préconiserais l'utilisation de classes de collection personnalisées.

Fondamentalement, C# 3.0 autorise toute classe qui implémente IEnumerable et dispose d'une méthode Add pour utiliser la nouvelle syntaxe d'initialisation d'agrégation.Par exemple, comme Dictionary définit une méthode Add(K key, V value), il est possible d'initialiser un dictionnaire en utilisant cette syntaxe :

var d = new Dictionary<string, int>
{
    {"hello", 0},
    {"the answer to life the universe and everything is:", 42}
};

L’avantage de cette fonctionnalité est qu’elle fonctionne pour ajouter des méthodes avec n’importe quel nombre d’arguments.Par exemple, étant donné cette collection :

class c1 : IEnumerable
{
    void Add(int x1, int x2, int x3)
    {
        //...
    }

    //...
}

il serait possible de l'initialiser comme ceci :

var x = new c1
{
    {1,2,3},
    {4,5,6}
}

Cela peut être très utile si vous devez créer des tableaux statiques d'objets complexes.Par exemple, si vous utilisiez simplement List<Customer> et vous vouliez créer une liste statique d'objets clients, vous devrez la créer comme ceci :

var x = new List<Customer>
{
    new Customer("Scott Wisniewski", "555-555-5555", "Seattle", "WA"),
    new Customer("John Doe", "555-555-1234", "Los Angeles", "CA"),
    new Customer("Michael Scott", "555-555-8769", "Scranton PA"),
    new Customer("Ali G", "", "Staines", "UK")
}

Cependant, si vous utilisez une collection personnalisée, comme celle-ci :

class CustomerList  : List<Customer>
{
    public void Add(string name, string phoneNumber, string city, string stateOrCountry)
    {
        Add(new Customer(name, phoneNumber, city, stateOrCounter));
    }
}

Vous pouvez ensuite initialiser la collection en utilisant cette syntaxe :

var customers = new CustomerList
{
    {"Scott Wisniewski", "555-555-5555", "Seattle", "WA"},
    {"John Doe", "555-555-1234", "Los Angeles", "CA"},
    {"Michael Scott", "555-555-8769", "Scranton PA"},
    {"Ali G", "", "Staines", "UK"}
}

Cela présente l'avantage d'être à la fois plus facile à saisir et à lire car il n'est pas nécessaire de retaper le nom du type d'élément pour chaque élément.L'avantage peut être particulièrement important si le type d'élément est long ou complexe.

Cela étant dit, cela n'est utile que si vous avez besoin de collections statiques de données définies dans votre application.Certains types d'applications, comme les compilateurs, les utilisent en permanence.D'autres, comme les applications de base de données classiques, ne le font pas car elles chargent toutes leurs données à partir d'une base de données.

Mon conseil serait que si vous avez besoin de définir une collection statique d'objets ou d'encapsuler l'interface de collection, créez une classe de collection personnalisée.Sinon, j'utiliserais simplement List<T> directement.

Autres conseils

C'est recommandé que dans les API publiques, il ne faut pas utiliser List<T>, mais utiliser Collection<T>

Si vous en héritez, ça devrait aller, autant que je sache.

Je préfère simplement utiliser List<BusinessObject>.Le taper ajoute simplement un passe-partout inutile au code. List<BusinessObject> est un type spécifique, ce n'est pas n'importe lequel List objet, donc il est toujours fortement typé.

Plus important encore, déclarer quelque chose List<BusinessObject> permet à tous ceux qui lisent le code de savoir plus facilement à quels types ils ont affaire, ils n'ont pas besoin de chercher pour comprendre à quoi correspond un BusinessObjectCollection est et rappelez-vous que ce n'est qu'une liste.En tapant, vous devrez exiger une convention de (re)nommage cohérente que tout le monde doit suivre pour que cela ait un sens.

Utilisez le type List<BusinessObject> où vous devez en déclarer une liste.Cependant, où vous retournez une liste de BusinessObject, pensez à revenir IEnumerable<T>, IList<T> ou ReadOnlyCollection<T> - c'est à dire.rendre le contrat le plus faible possible qui satisfasse le client.

Lorsque vous souhaitez « ajouter du code personnalisé » à une liste, codez les méthodes d’extension sur le type de liste.Encore une fois, attachez ces méthodes au contrat le plus faible possible, par ex.

public static int SomeCount(this IEnumerable<BusinessObject> someList)

Bien sûr, vous ne pouvez pas et ne devez pas ajouter d'état avec des méthodes d'extension, donc si vous devez ajouter une nouvelle propriété et un champ derrière elle, utilisez une sous-classe ou mieux, une classe wrapper pour stocker cela.

J'ai fait des allers-retours entre 2 options :

public class BusinessObjectCollection : List<BusinessObject> {}

ou des méthodes qui font simplement ce qui suit :

public IEnumerable<BusinessObject> GetBusinessObjects();

L'avantage de la première approche est que vous pouvez modifier le magasin de données sous-jacent sans avoir à vous soucier des signatures de méthode.Malheureusement, si vous héritez d'un type de collection qui supprime une méthode de l'implémentation précédente, vous devrez alors gérer ces situations tout au long de votre code.

Vous devriez probablement éviter de créer votre propre collection à cette fin.Il est assez courant de vouloir changer le type de structure de données plusieurs fois lors de refactorings ou lors de l'ajout de nouvelles fonctionnalités.Avec votre approche, vous vous retrouveriez avec une classe distincte pour BusinessObjectList, BusinessObjectDictionary, BusinessObjectTree, etc.

Je ne vois pas vraiment l'intérêt de créer cette classe simplement parce que le nom de la classe est plus lisible.Oui, la syntaxe des chevrons est un peu moche, mais elle est standard en C++, C# et Java, donc même si vous n'écrivez pas de code qui l'utilise, vous allez la rencontrer tout le temps.

Je ne dérive généralement mes propres classes de collection que si j'ai besoin de "ajouter de la valeur".Par exemple, si la collection elle-même devait être accompagnée de certaines propriétés de "métadonnées".

Je fais exactement la même chose que toi Jonathan...il suffit d'hériter de List<T>.Tu reçois le meilleur des deux mondes.Mais je ne le fais généralement que lorsqu'il y a une valeur à ajouter, comme ajouter un LoadAll() méthode ou autre.

Vous pouvez utiliser les deux.Pour la paresse - je veux dire la productivité - List est un cours très utile, il est aussi "complet" et franchement plein de membres YANGNI.Couplé à l'argument/recommandation sensé avancé par le Article MSDN déjà lié à l'exposition de List en tant que membre public, je préfère la "troisième" voie :

Personnellement, j'utilise le modèle décorateur pour exposer uniquement ce dont j'ai besoin dans List, c'est-à-dire :

public OrderItemCollection : IEnumerable<OrderItem> 
{
    private readonly List<OrderItem> _orderItems = new List<OrderItem>();

    void Add(OrderItem item)
    {
         _orderItems.Add(item)
    }

    //implement only the list members, which are required from your domain. 
    //ie. sum items, calculate weight etc...

    private IEnumerator<string> Enumerator() {
        return _orderItems.GetEnumerator();
    }

    public IEnumerator<string> GetEnumerator() {
        return Enumerator();
    }    
}

De plus, je ferais probablement abstraction de OrderItemCollection dans IOrderItemCollection afin de pouvoir échanger mon implémentation de IOrderItemCollection à l'avenir (je préférerai peut-être utiliser un autre objet énumérable interne tel que Collection ou plus probablement pour utiliser une collection ou un ensemble de paires de valeurs clés. .

J'utilise des listes génériques pour presque tous les scénarios.La seule fois où j’envisagerais d’utiliser une collection dérivée, c’est si j’ajoute des membres spécifiques à la collection.Cependant, l’avènement de LINQ a même réduit ce besoin.

6 sur 1, une demi-douzaine d'un autre

De toute façon c'est la même chose.Je ne le fais que lorsque j'ai des raisons d'ajouter du code personnalisé dans BusinessObjectCollection.

Sans que les méthodes de chargement renvoient une liste, cela me permet d'écrire plus de code dans une classe générique commune et de le faire fonctionner.Comme une méthode Load.

Comme quelqu'un d'autre l'a souligné, il est recommandé de ne pas exposer List publiquement, et FxCop se plaindra si vous le faites.Cela inclut l'héritage de List comme dans :

public MyTypeCollection : List<MyType>

Dans la plupart des cas, les API publiques exposeront IList (ou ICollection ou IEnumerable), selon le cas.

Dans les cas où vous souhaitez votre propre collection personnalisée, vous pouvez garder FxCop silencieux en héritant de Collection au lieu de List.

Si vous choisissez de créer votre propre classe de collection, vous devriez consulter les types dans Espace de noms System.Collections.ObjectModel.

L'espace de noms définit les classes de base qui permettent aux implémenteurs de créer plus facilement des collections personnalisées.

J'ai tendance à le faire avec ma propre collection si je veux protéger l'accès à la liste réelle.Lorsque vous écrivez des objets métier, il est probable que vous ayez besoin d'un hook pour savoir si votre objet est ajouté/supprimé, en ce sens, je pense que BOCollection est une meilleure idée.Bien sûr, si cela n’est pas nécessaire, List est plus léger.Vous souhaiterez peut-être également utiliser IList pour fournir une interface d'abstraction supplémentaire si vous avez besoin d'une sorte de proxy (par ex.une fausse collection déclenche un chargement paresseux depuis la base de données)

Mais...pourquoi ne pas envisager Castle ActiveRecord ou tout autre framework ORM mature ?:)

La plupart du temps, j'utilise simplement la méthode List, car elle me donne toutes les fonctionnalités dont j'ai besoin dans 90 % du temps, et lorsque quelque chose de "supplémentaire" est nécessaire, j'en hérite et je code ce bit supplémentaire. .

Je ferais ceci :

using BusinessObjectCollection = List<BusinessObject>;

Cela crée simplement un alias plutôt qu'un type complètement nouveau.Je préfère utiliser List<BusinessObject> directement car cela me laisse libre de modifier la structure sous-jacente de la collection à un moment donné dans le futur sans changer le code qui l'utilise (tant que je fournis les mêmes propriétés et méthodes).

essaye ça :

System.Collections.ObjectModel.Collection<BusinessObject>

cela rend inutile l'implémentation d'une méthode de base comme le fait CollectionBase

Ceci est le chemin:

renvoyer les tableaux, accepter IEnumerable<T>

=)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top