Dlaczego warto używać ICollection, a nie IEnumerable lub List <T> w relacjach wiele-wiele / jeden-wiele?

359

Widzę to bardzo często w tutorialach z właściwościami nawigacji jak ICollection<T>.

Czy jest to obowiązkowy wymóg dla Entity Framework? Czy mogę użyć IEnumerable?

Jaki jest główny cel używania ICollectionzamiast IEnumerablelub nawet List<T>?

Jan Carlo Viray
źródło

Odpowiedzi:

439

Zazwyczaj to, co wybierzesz, będzie zależeć od metod, do których potrzebujesz dostępu. Ogólnie - IEnumerable<>(MSDN: http://msdn.microsoft.com/en-us/library/system.collections.ienumerable.aspx ), aby uzyskać listę obiektów, które wymagają jedynie iteracji, ICollection<>(MSDN: http: // msdn.microsoft.com/en-us/library/92t2ye13.aspx ), aby uzyskać listę obiektów, które należy iterować i modyfikować, List<>oraz listę obiektów, które należy iterować, modyfikować, sortować itp. (patrz tutaj pełna lista: http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx ).

Z bardziej szczegółowego punktu widzenia leniwe ładowanie odgrywa rolę przy wybieraniu typu. Domyślnie właściwości nawigacji w Entity Framework są dostarczane ze śledzeniem zmian i są serwerami proxy. Aby dynamiczny serwer proxy mógł zostać utworzony jako właściwość nawigacji, typ wirtualny musi zostać zaimplementowany ICollection.

Właściwość nawigacji, która reprezentuje „wiele” końca relacji, musi zwracać typ, który implementuje ICollection, gdzie T jest typem obiektu na drugim końcu relacji. - Wymagania dotyczące tworzenia proxy POCO MSDN

Więcej informacji o definiowaniu i zarządzaniu relacjami MSDN

Travis J
źródło
2
więc z tym Listpowinno być o wiele lepiej, tak?
Jan Carlo Viray,
3
@JanCarloViray - Często używam List. Chociaż ma największy narzut, zapewnia największą funkcjonalność.
Travis J
1
Listy są definiowane bardziej przez ich indeksatory niż przez możliwość ich sortowania (posiadanie indeksatora liczb całkowitych ułatwia sortowanie, ale nie jest to wymagane).
phoog
2
Jeśli chodzi o edycję, ograniczenie właściwości do typu interfejsu nie dotyczy pamięci, ale enkapsulacji. Zastanów się: private IEnumerable<int> _integers = new List<int> { 1, 2, 3 };używa tej samej pamięci coprivate List<int> _integers = new List<int> { 1, 2, 3 };
phoog
13
@TravisJ: List<T>ma GetEnumerator()niezależną od implementacji metodę IEnumerable<T>, która zwraca zmienną strukturę typu List<T>.Enumerator. W większości kontekstów ten typ przyniesie nieco lepszą wydajność niż autonomiczny obiekt sterty. Kompilatory, które wyliczają typu kaczego (tak jak C # i vb.net) mogą skorzystać z tego podczas generowania foreachkodu. Jeśli List<T>rzutowanie IEnumrable<T>nastąpi przed foreach, IEnumerable<T>.GetEnumerator()metoda zwróci obiekt przydzielony do sterty, uniemożliwiając optymalizację.
supercat
85

ICollection<T>jest używany, ponieważ IEnumerable<T>interfejs nie umożliwia dodawania elementów, usuwania elementów ani modyfikowania kolekcji w inny sposób.

Justin Niessner
źródło
3
co powiesz na porównanie z Listą <T>?
Jan Carlo Viray,
12
List<T>wdraża ICollection<T>.
spędzić
To, co ogólne ICollection, nie pozwala w żaden sposób dodawać elementów, ale nadal jest przydatnym dodatkiem, IEnumerable<T>ponieważ zapewnia Countczłonka, który jest zwykle znacznie szybszy niż wyliczanie wszystkiego. Zauważ, że jeśli do IList<Cat>lub ICollection<Cat>kod zostanie przekazany w oczekiwaniu na IEnumerable<Animal>, Count()metoda rozszerzenia będzie szybka, jeśli zaimplementuje ona nie-rodzajowe ICollection, ale nie, jeśli zaimplementuje tylko ogólne interfejsy, ponieważ typowe ICollection<Cat>nie zostanie zaimplementowane ICollection<Animal>.
supercat
58

Odpowiadając na twoje pytanie dotyczące List<T>:

List<T>jest klasą; określenie interfejsu pozwala na większą elastyczność implementacji. Lepszym pytaniem jest „dlaczego nie IList<T>?”

Aby odpowiedzieć na to pytanie, zastanów się, co IList<T>dodaje do ICollection<T>: indeksowanie liczb całkowitych, co oznacza, że ​​elementy mają pewną dowolną kolejność i można je odzyskać przez odniesienie do tej kolejności. Prawdopodobnie nie ma to większego znaczenia w większości przypadków, ponieważ elementy prawdopodobnie trzeba zamówić inaczej w różnych kontekstach.

phoog
źródło
21

Istnieją pewne podstawowe różnice między ICollection a IEnumerable

  • IEnumerable - zawiera tylko metodę GetEnumerator, aby uzyskać Enumerator i umożliwia zapętlanie
  • ICollection zawiera dodatkowe metody: Dodaj, Usuń, Zawiera, Policz, Kopiuj do
  • ICollection jest dziedziczony z IEnumerable
  • Dzięki ICollection możesz modyfikować kolekcję za pomocą metod takich jak dodawanie / usuwanie. Nie masz możliwości robienia tego samego z IEnumerable.

Prosty program:

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;
        }
    }
}
Ramakrishnan
źródło
13

Pamiętam to w ten sposób:

  1. IEnumerable ma jedną metodę GetEnumerator (), która pozwala czytać wartości w kolekcji, ale nie zapisywać do niej. Większość złożoności korzystania z modułu wyliczającego zajmuje się dla każdego wyrażenia w języku C #. IEnumerable ma jedną właściwość: Current, która zwraca bieżący element.

  2. ICollection implementuje IEnumerable i dodaje kilka dodatkowych właściwości, z których większość używa Count. Ogólna wersja ICollection implementuje metody Add () i Remove ().

  3. IList implementuje zarówno IEnumerable, jak i ICollection, i dodaje dostęp do indeksowania liczb całkowitych do elementów (co zwykle nie jest wymagane, ponieważ porządkowanie odbywa się w bazie danych).

użytkownik3918295
źródło
4
Na podstawie tego, co napisałeś ICollection i IList są takie same. Dodaj to, co zostało dodane do IList, które nie istnieje w ICollection.
zakłady
ICollection VS IList, tylko interfejs IList w System.Collection, który zawiera wszystkie funkcje IEnumerable i ICollection oraz dodatkową funkcjonalność. IList ma metody wstawiania i usuwania. Obie metody akceptują indeks w swoim parametrze. Obsługuje więc operacje oparte na indeksie nad kolekcją.
E. Meir
7

Podstawową ideą użycia ICollectionjest zapewnienie interfejsu umożliwiającego dostęp tylko do odczytu do pewnej skończonej ilości danych. W rzeczywistości masz właściwość ICollection.Count . IEnumerablejest bardziej odpowiedni dla niektórych łańcuchów danych, w których czytasz do pewnego logicznego punktu, niektórych warunków wyraźnie określonych przez konsumenta lub do końca wyliczenia.

Tigran
źródło
14
TIL, który ICollectionjest tylko do odczytu, podczas gdy ICollection<T>nie jest.
Carl G
2

Właściwości nawigacji są zazwyczaj definiowane jako wirtualne, dzięki czemu mogą korzystać z niektórych funkcji Entity Framework, takich jak leniwe ładowanie.

Jeśli właściwość nawigacji może przechowywać wiele encji (jak w relacjach wiele do wielu lub jeden do wielu), jej typem musi być lista, w której wpisy można dodawać, usuwać i aktualizować, takie jak 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- aplikacja mvc

LewisAntonio803
źródło
2

Co mam zrobić w przeszłości jest zadeklarować moje zbiory klasa wewnętrzna użyciu IList<Class>, ICollection<Class>lub IEnumerable<Class>(jeśli listy statycznej) w zależności od tego, czy nie będę musiał zrobić dowolną liczbę z następujących czynności w metodzie w moim repozytorium: wyliczyć, sortowania / zamówienia lub modyfikowania . Kiedy po prostu muszę wyliczyć (i być może posortować) obiekty, tworzę temp List<Class>do pracy z kolekcją w ramach metody IEnumerable. Myślę, że ta praktyka byłaby skuteczna tylko wtedy, gdy kolekcja jest stosunkowo niewielka, ale ogólnie może być dobrą praktyką, idk. Proszę mnie poprawić, jeśli istnieją dowody, dlaczego nie jest to dobra praktyka.

yardpenalty.com
źródło
0

Spróbujmy myśleć nieszablonowo za pomocą logiki / i zrozumieć dokładnie te trzy interfejsy w pytaniu:

Gdy klasa niektórych instancji implementuje interfejs System.Collection.IEnumerable, to w prostych słowach możemy powiedzieć, że ta instancja jest zarówno policzalna, jak i iterowalna, co oznacza, że ​​ta instancja pozwala w jakiś sposób w jednej pętli przejść / uzyskać / przekazać / przeglądaj / iteruj po / przez wszystkie elementy i elementy, które zawiera ta instancja.

Oznacza to, że można również wyliczyć wszystkie elementy i elementy, które zawiera ta instancja.

Każda klasa implementująca interfejs System.Collection.IEnumerable implementuje również metodę GetEnumerator, która nie przyjmuje argumentów i zwraca instancję System.Collections.IEnumerator.

Instancje interfejsu System.Collections.IEnumerator zachowują się bardzo podobnie do iteratorów C ++.

Kiedy klasa niektórych instancji implementuje interfejs System.Collection.ICollection, to w prostych słowach możemy powiedzieć, że ta instancja jest zbiorem rzeczy.

Ogólna wersja tego interfejsu, tj. System.Collection.Generic.ICollection, jest bardziej informacyjna, ponieważ ten ogólny interfejs wyraźnie określa, jaki jest typ rzeczy w kolekcji.

Wszystko to jest rozsądne, racjonalne, logiczne i ma sens, że interfejs System.Collections.ICollection dziedziczy po interfejsie System.Collections.IEnumerable, ponieważ teoretycznie każda kolekcja jest zarówno wymienna, jak i iterowalna, a teoretycznie możliwe jest przejrzenie wszystkich elementów i elementów w każdej kolekcji.

Interfejs System.Collections.ICollection reprezentuje skończoną kolekcję dynamiczną, którą można zmieniać, co oznacza, że ​​istniejące elementy można usunąć z kolekcji, a nowe elementy można dodać do tej samej kolekcji.

To wyjaśnia, dlaczego interfejs System.Collections.ICollection ma metody „Dodaj” i „Usuń”.

Ponieważ instancje interfejsu System.Collections.ICollection są kolekcjami skończonymi, słowo „skończone” oznacza, że ​​każda kolekcja tego interfejsu ma zawsze skończoną liczbę elementów i elementów.

Właściwość Count interfejsu System.Collections.ICollection zakłada zwrócenie tej liczby.

Interfejs System.Collections.IEnumerable nie ma tych metod i właściwości, jakie ma interfejs System.Collections.ICollection, ponieważ nie ma sensu, aby System.Collections.IEnumerable miał te metody i właściwości, które ma interfejs System.Collections.ICollection.

Logika mówi również, że każda instancja, która jest zarówno policzalna, jak i iterowalna, niekoniecznie jest kolekcją i niekoniecznie jest zmienna.

Kiedy mówię zmienny, mam na myśli to, że nie od razu myślę, że możesz dodać lub usunąć coś z czegoś, co jest zarówno policzalne, jak i iterowalne.

Na przykład, jeśli właśnie utworzyłem skończoną sekwencję liczb pierwszych, ta skończona sekwencja liczb pierwszych jest rzeczywiście instancją interfejsu System.Collections.IEnumerable, ponieważ teraz mogę przejrzeć wszystkie liczby pierwsze w tej skończonej sekwencji w jednej pętli i rób wszystko, co chcę zrobić z każdym z nich, na przykład drukując każdy z nich do okna lub ekranu konsoli, ale ta skończona sekwencja liczb pierwszych nie jest instancją interfejsu System.Collections.ICollection, ponieważ nie ma sensu dodaj liczby złożone do tej skończonej sekwencji liczb pierwszych.

Również chcesz, aby w następnej iteracji uzyskać następną najbliższą większą liczbę pierwszą do bieżącej liczby pierwszej w bieżącej iteracji, jeśli tak, nie chcesz również usuwać istniejących liczb pierwszych z tej skończonej sekwencji liczb pierwszych.

Prawdopodobnie chcesz także użyć, zakodować i napisać „return return” w metodzie GetEnumerator interfejsu System.Collections.IEnumerable w celu wygenerowania liczb pierwszych i nieprzydzielania niczego na stercie pamięci, a następnie zadania Garbage Collector (GC) do obu zwolnij i zwolnij tę pamięć ze sterty, ponieważ jest to oczywiście zarówno marnowanie pamięci systemu operacyjnego, jak i zmniejszenie wydajności.

Dynamiczne przydzielanie i zwalnianie pamięci na stercie powinno odbywać się podczas wywoływania metod i właściwości interfejsu System.Collections.ICollection, ale nie podczas wywoływania metod i właściwości interfejsu System.Collections.IEnumerable (chociaż interfejs System.Collections.IEnumerable ma tylko 1 metoda i 0 właściwości).

Zgodnie z tym, co powiedzieli inni na tej stronie przepełnienia stosu, interfejs System.Collections.IList po prostu reprezentuje porządek kolekcję do co wyjaśnia, dlaczego metody interfejsu System.Collections.IList działają z indeksami w przeciwieństwie do interfejsu System.Collections.ICollection.

W skrócie interfejs System.Collections.ICollection nie oznacza, że ​​jego instancję można zamówić, ale interfejs System.Collections.IList implikuje to.

Zestaw teoretycznie uporządkowany to specjalny przypadek zestawu nieuporządkowanego.

Ma to również sens i wyjaśnia, dlaczego interfejs System.Collections.IList dziedziczy interfejs System.Collections.ICollection.

użytkownik 11336341
źródło