Pourquoi utiliser ICollection et non IEnumerable ou List <T> sur des relations plusieurs-plusieurs / un-plusieurs?


359

Je vois cela beaucoup dans les tutoriels, avec des propriétés de navigation comme ICollection<T>.

Est-ce une exigence obligatoire pour Entity Framework? Puis-je utiliser IEnumerable?

Quel est le principal objectif de l'utilisation ICollectionau lieu de IEnumerableou même List<T>?

Réponses:


440

Habituellement, ce que vous choisissez dépend des méthodes auxquelles vous devez accéder. En général - IEnumerable<>(MSDN: http://msdn.microsoft.com/en-us/library/system.collections.ienumerable.aspx ) pour une liste des objets qui doivent uniquement être itérés via, ICollection<>(MSDN: http: // msdn.microsoft.com/en-us/library/92t2ye13.aspx ) pour une liste des objets qui doivent être itérés et modifiés, List<>pour une liste des objets qui doivent être itérés, modifiés, triés, etc. (voir ici pour une liste complète: http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx ).

D'un point de vue plus spécifique, le chargement paresseux entre en jeu avec le choix du type. Par défaut, les propriétés de navigation dans Entity Framework sont fournies avec le suivi des modifications et sont des proxys. Pour que le proxy dynamique soit créé en tant que propriété de navigation, le type virtuel doit implémenter ICollection.

Une propriété de navigation qui représente l'extrémité "plusieurs" d'une relation doit renvoyer un type qui implémente ICollection, où T est le type de l'objet à l'autre extrémité de la relation. - Conditions requises pour la création de proxy POCO MSDN

Plus d'informations sur la définition et la gestion des relations MSDN


2
donc, avec ça, ça Listdevrait être beaucoup mieux, ouais?
Jan Carlo Viray

3
@JanCarloViray - J'ai tendance à en utiliser Listbeaucoup. Bien qu'il ait le plus de frais généraux, il offre le plus de fonctionnalités.
Travis J

1
Les listes sont définies plus par leurs indexeurs que par la possibilité de les trier (avoir un indexeur entier facilite le tri de quelque chose, mais ce n'est pas une exigence).
phoog

2
En ce qui concerne votre modification, restreindre une propriété à un type d'interface ne concerne pas la mémoire mais l'encapsulation. Considérez: private IEnumerable<int> _integers = new List<int> { 1, 2, 3 };utilise la même mémoire queprivate List<int> _integers = new List<int> { 1, 2, 3 };
phoog

13
@TravisJ: List<T>possède une GetEnumerator()méthode, distincte de son implémentation de IEnumerable<T>, qui renvoie un type de structure mutable List<T>.Enumerator. Dans la plupart des contextes, ce type donnera des performances légèrement meilleures qu'un objet de segment de mémoire autonome. Les compilateurs dont les énumérateurs de type canard (comme le font C # et vb.net) peuvent en profiter lors de la génération de foreachcode. Si le List<T>est converti en IEnumrable<T>avant le foreach, la IEnumerable<T>.GetEnumerator()méthode retournera un objet alloué au tas, rendant l'optimisation impossible.
supercat

86

ICollection<T>est utilisé car l' IEnumerable<T>interface ne fournit aucun moyen d'ajouter des éléments, de supprimer des éléments ou de modifier autrement la collection.


3
qu'en est-il de la comparaison avec la liste <T>?
Jan Carlo Viray

12
List<T>met en œuvre ICollection<T>.
passer le

Le non générique ICollectionne permet aucun moyen d'ajouter des éléments, mais c'est toujours un complément utile IEnumerable<T>car il fournit un Countmembre qui est généralement beaucoup plus rapide que d'énumérer tout. Notez que si un IList<Cat>ou ICollection<Cat>est passé au code qui attend un IEnumerable<Animal>, la Count()méthode d'extension sera rapide si elle implémente le non générique ICollection, mais pas si elle implémente uniquement les interfaces génériques car un typique ICollection<Cat>ne l'implémentera pas ICollection<Animal>.
supercat

58

Répondre à votre question sur List<T>:

List<T>est une classe; la spécification d'une interface permet une plus grande flexibilité d'implémentation. Une meilleure question est "pourquoi pas IList<T>?"

Pour répondre à cette question, considérez ce qui IList<T>s'ajoute à ICollection<T>: l'indexation entière, ce qui signifie que les éléments ont un ordre arbitraire et peuvent être récupérés par référence à cet ordre. Cela n'est probablement pas significatif dans la plupart des cas, car les articles doivent probablement être commandés différemment dans différents contextes.


21

Il existe des différences de base entre ICollection et IEnumerable

  • IEnumerable - contient uniquement la méthode GetEnumerator pour obtenir Enumerator et autorise le bouclage
  • ICollection contient des méthodes supplémentaires: Add, Remove, Contains, Count, CopyTo
  • ICollection est hérité de IEnumerable
  • Avec ICollection, vous pouvez modifier la collection en utilisant les méthodes comme ajouter / supprimer. Vous n'avez pas la liberté de faire de même avec IEnumerable.

Programme simple:

using System;
using System.Collections;
using System.Collections.Generic;

namespace StackDemo
{
    class Program 
    {
        static void Main(string[] args)
        {
            List<Person> persons = new List<Person>();
            persons.Add(new Person("John",30));
            persons.Add(new Person("Jack", 27));

            ICollection<Person> personCollection = persons;
            IEnumerable<Person> personEnumeration = persons;

            // IEnumeration
            // IEnumration Contains only GetEnumerator method to get Enumerator and make a looping
            foreach (Person p in personEnumeration)
            {                                   
               Console.WriteLine("Name:{0}, Age:{1}", p.Name, p.Age);
            }

            // ICollection
            // ICollection Add/Remove/Contains/Count/CopyTo
            // ICollection is inherited from IEnumerable
            personCollection.Add(new Person("Tim", 10));

            foreach (Person p in personCollection)
            {
                Console.WriteLine("Name:{0}, Age:{1}", p.Name, p.Age);        
            }
            Console.ReadLine();

        }
    }

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public Person(string name, int age)
        {
            this.Name = name;
            this.Age = age;
        }
    }
}

13

Je m'en souviens de cette façon:

  1. IEnumerable a une méthode GetEnumerator () qui permet de lire les valeurs d'une collection sans y écrire. La plupart de la complexité de l'utilisation de l'énumérateur est prise en charge pour nous par le pour chaque instruction en C #. IEnumerable a une propriété: Current, qui renvoie l'élément actuel.

  2. ICollection implémente IEnumerable et ajoute quelques propriétés supplémentaires dont la plus utilisée est Count. La version générique de ICollection implémente les méthodes Add () et Remove ().

  3. IList implémente à la fois IEnumerable et ICollection et ajoute l'accès d'indexation d'entier aux éléments (ce qui n'est généralement pas requis, car la commande est effectuée dans la base de données).


4
D'après ce que vous avez écrit, ICollection et IList sont les mêmes. Veuillez ajouter ce qui est ajouté à IList qui n'existe pas dans ICollection.
paris

ICollection VS IList, interface IList uniquement dans System.Collection qui contient toutes les fonctionnalités de IEnumerable et ICollection et des fonctionnalités supplémentaires. IList a des méthodes d'insertion et de suppression. Les deux méthodes acceptent l'index dans leur paramètre. Ainsi, il prend en charge les opérations basées sur l'index sur la collection.
E.Meir

7

L'idée de base de l'utilisation ICollectionest de fournir une interface pour accéder en lecture seule à une quantité limitée de données. En fait, vous avez une propriété ICollection.Count . IEnumerableconvient mieux à une chaîne de données où vous lisez jusqu'à un certain point logique, une condition spécifiée explicitement par le consommateur ou jusqu'à la fin de l'énumération.


14
TIL qui ICollectionest en lecture seule alors qu'il ICollection<T>ne l'est pas.
Carl G

2

Les propriétés de navigation sont généralement définies comme virtuelles afin qu'elles puissent tirer parti de certaines fonctionnalités d'Entity Framework telles que le chargement paresseux.

Si une propriété de navigation peut contenir plusieurs entités (comme dans les relations plusieurs-à-plusieurs ou un-à-plusieurs), son type doit être une liste dans laquelle des entrées peuvent être ajoutées, supprimées et mises à jour, comme ICollection.

https://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net- application mvc


2

Ce que j'ai fait dans le passé, c'est déclarer mes collections de classes internes en utilisant IList<Class>, ICollection<Class>ou IEnumerable<Class>(si la liste est statique) selon que je devrai ou non effectuer un certain nombre des opérations suivantes dans une méthode de mon référentiel: énumérer, trier / ordonner ou modifier . Lorsque j'ai juste besoin d'énumérer (et peut-être de trier) des objets, je crée un temp List<Class>pour travailler avec la collection dans une méthode IEnumerable. Je pense que cette pratique ne serait efficace que si la collection est relativement petite, mais cela peut être une bonne pratique en général, idk. Veuillez me corriger s'il existe des preuves expliquant pourquoi cela ne serait pas une bonne pratique.


0

Essayons de penser en dehors de la boîte avec / par logique et de comprendre clairement ces trois interfaces dans votre question:

Lorsque la classe d'une instance implémente l'interface System.Collection.IEnumerable, en termes simples, nous pouvons dire que cette instance est à la fois énumérable et itérable, ce qui signifie que cette instance permet en quelque sorte dans une seule boucle d'aller / obtenir / passer / traverser / itérer sur / à travers tous les éléments et éléments que cette instance contient.

Cela signifie qu'il est également possible d'énumérer tous les éléments et éléments que cette instance contient.

Chaque classe qui implémente l'interface System.Collection.IEnumerable implémente également la méthode GetEnumerator qui ne prend aucun argument et renvoie une instance System.Collections.IEnumerator.

Les instances de l'interface System.Collections.IEnumerator se comportent de manière très similaire aux itérateurs C ++.

Lorsque la classe d'une instance implémente l'interface System.Collection.ICollection, alors, en termes simples, nous pouvons dire que cette instance est une collection de choses.

La version générique de cette interface, à savoir System.Collection.Generic.ICollection, est plus informative car cette interface générique indique explicitement quel est le type des éléments de la collection.

Tout cela est raisonnable, rationnel, logique et il est logique que l'interface System.Collections.ICollection hérite de l'interface System.Collections.IEnumerable, car théoriquement chaque collection est également à la fois énumérable et itérable et il est théoriquement possible de parcourir tous les éléments et éléments dans chaque collection.

L'interface System.Collections.ICollection représente une collection dynamique finie modifiable, ce qui signifie que les éléments existants peuvent être supprimés de la collection et que de nouveaux éléments peuvent être ajoutés à la même collection.

Cela explique pourquoi l'interface System.Collections.ICollection a les méthodes "Ajouter" et "Supprimer".

Étant donné que les instances de l'interface System.Collections.ICollection sont des collections finies, le mot "fini" implique que chaque collection de cette interface contient toujours un nombre fini d'éléments et d'éléments.

L'interface Count de System.Collections.ICollection de propriété suppose de renvoyer ce nombre.

L'interface System.Collections.IEnumerable n'a pas ces méthodes et propriétés que l'interface System.Collections.ICollection possède, car cela n'a aucun sens que System.Collections.IEnumerable aura ces méthodes et propriétés que l'interface System.Collections.ICollection possède.

La logique dit également que chaque instance à la fois énumérable et itérable n'est pas nécessairement une collection et pas nécessairement modifiable.

Quand je dis modifiable, je veux dire que vous ne pensez pas immédiatement que vous pouvez ajouter ou supprimer quelque chose à la fois énumérable et itérable.

Si je viens de créer une séquence finie de nombres premiers, par exemple, cette séquence finie de nombres premiers est en effet une instance de l'interface System.Collections.IEnumerable, car maintenant je peux parcourir tous les nombres premiers de cette séquence finie dans une seule boucle et faire tout ce que je veux faire avec chacun d'eux, comme imprimer chacun d'eux dans la fenêtre ou l'écran de la console, mais cette séquence finie de nombres premiers n'est pas une instance de l'interface System.Collections.ICollection, car cela n'a pas de sens pour ajouter des nombres composites à cette séquence finie de nombres premiers.

De plus, vous voulez que dans la prochaine itération, le premier nombre supérieur le plus proche soit le nombre premier actuel dans l'itération actuelle, si c'est le cas, vous ne voulez pas non plus supprimer les nombres premiers existants de cette séquence finie de nombres premiers.

Vous souhaitez également probablement utiliser, coder et écrire "yield return" dans la méthode GetEnumerator de l'interface System.Collections.IEnumerable pour produire les nombres premiers et ne rien allouer sur le tas de mémoire, puis charger le Garbage Collector (GC) à la fois désallouer et libérer cette mémoire du tas, car cela est évidemment à la fois un gaspillage de mémoire du système d'exploitation et une diminution des performances.

L'allocation de mémoire dynamique et la désallocation sur le tas doivent être effectuées lors de l'appel des méthodes et des propriétés de l'interface System.Collections.ICollection, mais pas lors de l'appel des méthodes et des propriétés de l'interface System.Collections.IEnumerable (bien que l'interface System.Collections.IEnumerable n'ait que 1 méthode et 0 propriétés).

Selon ce que d'autres ont dit dans cette page Web Stack Overflow, l'interface System.Collections.IList représente simplement une collection commandable et cela explique pourquoi les méthodes de l'interface System.Collections.IList fonctionnent avec des index contrairement à celles de l'interface System.Collections.ICollection.

En bref, l'interface System.Collections.ICollection n'implique pas qu'une instance de celle-ci peut être commandée, mais l'interface System.Collections.IList implique cela.

L'ensemble théoriquement ordonné est un cas spécial d'ensemble non ordonné.

Cela a également du sens et explique pourquoi l'interface System.Collections.IList hérite de l'interface System.Collections.ICollection.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.