Por que usar ICollection e não IEnumerable ou List <T> em relacionamentos muitos-muitos / um-muitos?

359

Eu vejo isso muito nos tutoriais, com propriedades de navegação como ICollection<T> .

Esse é um requisito obrigatório para o Entity Framework? Posso usar IEnumerable?

Qual é o principal objetivo de usar em ICollectionvez de IEnumerableou mesmo List<T>?

Jan Carlo Viray
fonte

Respostas:

440

Geralmente, o que você escolhe depende de quais métodos você precisa acessar. Em geral - IEnumerable<>(MSDN: http://msdn.microsoft.com/en-us/library/system.collections.ienumerable.aspx ) para obter uma lista de objetos que precisam apenas ser iterados ICollection<>(MSDN: http: // msdn.microsoft.com/en-us/library/92t2ye13.aspx ) para obter uma lista de objetos que precisam ser iterados e modificados, List<>para obter uma lista de objetos que precisam ser iterados, modificados, classificados etc. (Veja aqui para obter uma lista completa: http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx ).

De um ponto de vista mais específico, o carregamento lento entra em jogo com a escolha do tipo. Por padrão, as propriedades de navegação no Entity Framework vêm com rastreamento de alterações e são proxies. Para que o proxy dinâmico seja criado como uma propriedade de navegação, o tipo virtual deve ser implementado ICollection.

Uma propriedade de navegação que representa a extremidade "muitos" de um relacionamento deve retornar um tipo que implementa ICollection, em que T é o tipo do objeto na outra extremidade do relacionamento. - Requisitos para a criação de proxies POCO MSDN

Mais informações em Definindo e gerenciando relacionamentos MSDN

Travis J
fonte
2
então, com isso, Listdeveria ser muito melhor, né?
Jan Carlo Viray
3
@JanCarloViray - eu costumo usar Listmuito. Embora tenha a maior sobrecarga, fornece a maior funcionalidade.
Travis J
11
As listas são definidas mais pelos indexadores do que pela capacidade de classificá-las (ter um indexador inteiro facilita a classificação de algo, mas não é um requisito).
Phoog
2
Com relação à sua edição, restringir uma propriedade a um tipo de interface não é sobre memória, mas sobre encapsulamento. Considere: private IEnumerable<int> _integers = new List<int> { 1, 2, 3 };usa a mesma memóriaprivate List<int> _integers = new List<int> { 1, 2, 3 };
phoog
13
@ TravisJ: List<T>possui um GetEnumerator()método, separado de sua implementação de IEnumerable<T>, que retorna um tipo de estrutura mutável List<T>.Enumerator. Na maioria dos contextos, esse tipo produzirá um desempenho um pouco melhor do que um objeto de heap independente. Compiladores que enumeradores do tipo pato (como C # e vb.net fazem) podem tirar proveito disso ao gerar foreachcódigo. Se List<T>for convertido para IEnumrable<T>antes de foreach, o IEnumerable<T>.GetEnumerator()método retornará um objeto alocado para heap, impossibilitando a otimização.
Supercat 10/12 /
86

ICollection<T>é usado porque a IEnumerable<T>interface não oferece nenhuma maneira de adicionar itens, remover itens ou modificar a coleção.

Justin Niessner
fonte
3
Que tal comparar com a Lista <T>?
Jan Carlo Viray
12
List<T>implementa ICollection<T>.
gastador
O não genérico ICollectionnão permite adicionar itens, mas ainda é um complemento útil, IEnumerable<T>pois fornece um Countmembro que normalmente é muito mais rápido do que enumerar tudo. Observe que se um IList<Cat>ou ICollection<Cat>for passado para o código esperando um IEnumerable<Animal>, o Count()método de extensão será rápido se implementar o não genérico ICollection, mas não se implementar apenas as interfaces genéricas, pois um típico ICollection<Cat>não será implementado ICollection<Animal>.
Supercat
58

Respondendo à sua pergunta sobre List<T>:

List<T>é uma classe; especificar uma interface permite mais flexibilidade de implementação. Uma pergunta melhor é "por que nãoIList<T> ?"

Para responder a essa pergunta, considere o que IList<T>contribui para ICollection<T>: indexação inteira, o que significa que os itens têm alguma ordem arbitrária e podem ser recuperados por referência a essa ordem. Provavelmente, isso não é significativo na maioria dos casos, pois os itens provavelmente precisam ser pedidos de maneira diferente em contextos diferentes.

phoog
fonte
21

Existem algumas diferenças básicas entre ICollection e IEnumerable

  • IEnumerable - contém apenas o método GetEnumerator para obter o Enumerator e permite loop
  • ICollection contém métodos adicionais: Adicionar, Remover, Contém, Contar, CopyTo
  • ICollection é herdado de IEnumerable
  • Com o ICollection, você pode modificar a coleção usando os métodos como adicionar / remover. Você não tem a liberdade de fazer o mesmo com o IEnumerable.

Programa Simples:

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
fonte
13

Lembro-me assim:

  1. IEnumerable possui um método GetEnumerator () que permite ler os valores em uma coleção, mas não gravar nele. A maior parte da complexidade do uso do enumerador é resolvida pela instrução for for each em C #. IEnumerable possui uma propriedade: Current, que retorna o elemento atual.

  2. ICollection implementa IEnumerable e adiciona poucas propriedades adicionais, das quais a maioria é usada Count. A versão genérica do ICollection implementa os métodos Add () e Remove ().

  3. O IList implementa IEnumerable e ICollection e adiciona o acesso de indexação inteira aos itens (o que geralmente não é necessário, pois a encomenda é feita no banco de dados).

user3918295
fonte
4
Com base no que você escreveu, ICollection e IList são os mesmos. Por favor, adicione o que é adicionado ao IList que não existe no ICollection.
aposta
ICollection VS IList, interface somente IList no System.Collection que contém todas as funcionalidades de IEnumerable e ICollection e funcionalidade adicional. O IList possui os métodos Insert e Remove. Ambos os métodos aceitam o índice em seus parâmetros. Portanto, ele suporta operações baseadas em índice sobre coleta.
E.Meir
7

A idéia básica do uso ICollectioné fornecer uma interface para acessar apenas uma quantidade finita de dados. Na verdade, você tem uma propriedade ICollection.Count . IEnumerableé mais adequado para alguma cadeia de dados em que você lê até algum ponto lógico, alguma condição especificada explicitamente pelo consumidor ou até o final da enumeração.

Tigran
fonte
14
TIL que ICollectioné somente leitura enquanto ICollection<T>não é.
Carl G
2

As propriedades de navegação são normalmente definidas como virtuais, para que possam tirar proveito de determinadas funcionalidades do Entity Framework, como carregamento lento.

Se uma propriedade de navegação puder conter várias entidades (como em relacionamentos muitos para muitos ou um para muitos), seu tipo deverá ser uma lista na qual as entradas possam ser adicionadas, excluídas e atualizadas, como 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- mvc-application

LewisAntonio803
fonte
2

O que eu fiz no passado é declarar minhas coleções de classes internas usando IList<Class>, ICollection<Class>ou IEnumerable<Class>(se estática), dependendo se devo ou não fazer qualquer número do seguinte em um método no meu repositório: enumerar, classificar / ordenar ou modificar . Quando só preciso enumerar (e talvez classificar) objetos, crio um temp List<Class>para trabalhar com a coleção dentro de um método IEnumerable. Eu acho que essa prática só seria eficaz se a coleção for relativamente pequena, mas pode ser uma boa prática em geral, idk. Corrija-me se houver evidências de por que isso não seria uma boa prática.

yardpenalty.com
fonte
0

Vamos tentar pensar fora da caixa com / pela lógica e entender claramente essas três interfaces na sua pergunta:

Quando a classe de alguma instância implementa a interface System.Collection.IEnumerable, então, em palavras simples, podemos dizer que essa instância é enumerável e iterável, o que significa que essa instância permite de alguma forma em um único loop ir / obter / passar / percorra / itere sobre / através de todos os itens e elementos que esta instância contém.

Isso significa que também é possível enumerar todos os itens e elementos que esta instância contém.

Toda classe que implementa a interface System.Collection.IEnumerable também implementa o método GetEnumerator que não usa argumentos e retorna uma instância de System.Collections.IEnumerator.

Instâncias da interface System.Collections.IEnumerator se comportam muito semelhantes aos iteradores do C ++.

Quando a classe de alguma instância implementa a interface System.Collection.ICollection, em palavras simples, podemos dizer que essa instância é uma coleção de coisas.

A versão genérica dessa interface, ou seja, System.Collection.Generic.ICollection, é mais informativa porque essa interface genérica declara explicitamente qual é o tipo de itens na coleção.

Tudo isso é razoável, racional, lógico e faz sentido que a interface System.Collections.ICollection herda da interface System.Collections.IEnumerable, porque teoricamente toda coleção também é enumerável e iterável e, teoricamente, é possível passar por todos os itens e elementos em toda coleção.

A interface System.Collections.ICollection representa uma coleção dinâmica finita que pode ser alterada, o que significa que os itens existentes podem ser removidos da coleção e novos itens podem ser adicionados à mesma coleção.

Isso explica por que a interface System.Collections.ICollection possui os métodos "Adicionar" e "Remover".

Como essas instâncias da interface System.Collections.ICollection são coleções finitas, a palavra "finito" implica que toda coleção dessa interface sempre possui um número finito de itens e elementos.

A propriedade Count da interface System.Collections.ICollection supõe retornar esse número.

A interface System.Collections.IEnumerable não possui esses métodos e propriedades que a interface System.Collections.ICollection possui, porque não faz sentido que System.Collections.IEnumerable tenha esses métodos e propriedades que a interface System.Collections.ICollection possui.

A lógica também diz que toda instância que é enumerável e iterável não é necessariamente uma coleção e não é necessariamente mutável.

Quando digo mutável, quero dizer que não pense imediatamente que você pode adicionar ou remover algo de algo que é enumerável e iterável.

Se eu apenas criei uma sequência finita de números primos, por exemplo, essa sequência finita de números primos é de fato uma instância da interface System.Collections.IEnumerable, porque agora posso revisar todos os números primos dessa sequência finita em um único loop e faça o que eu quiser com cada um deles, como imprimir cada um deles na janela ou na tela do console, mas essa sequência finita de números primos não é uma instância da interface System.Collections.ICollection, porque isso não faz sentido. adicione números compostos a essa sequência finita de números primos.

Além disso, na próxima iteração você deseja obter o próximo número primo maior e mais próximo do número primo atual na iteração atual. Caso contrário, também não deseja remover os números primos existentes dessa sequência finita de números primos.

Além disso, você provavelmente deseja usar, codificar e escrever "yield return" no método GetEnumerator da interface System.Collections.IEnumerable para produzir os números primos e não alocar nada no heap da memória e depois executar tarefas no Garbage Collector (GC) para ambos. desalocar e liberar essa memória do heap, porque obviamente isso é desperdício de memória do sistema operacional e diminui o desempenho.

A alocação e desalocação dinâmica de memória no heap devem ser feitas ao invocar os métodos e propriedades da interface System.Collections.ICollection, mas não ao invocar os métodos e propriedades da interface System.Collections.IEnumerable (embora a interface System.Collections.IEnumerable tenha apenas 1 método e 0 propriedades).

De acordo com o que outros disseram nesta página do Stack Overflow, a interface System.Collections.IList simplesmente representa um pedido coleção e isso explica por que os métodos da interface System.Collections.IList funcionam com índices em contraste com os da interface System.Collections.ICollection.

Em resumo, a interface System.Collections.ICollection não implica que uma instância dela possa ser solicitada, mas a interface System.Collections.IList implica isso.

Conjunto teórico ordenado é um caso especial de conjunto não ordenado.

Isso também faz sentido e explica por que a interface System.Collections.IList herda a interface System.Collections.ICollection.

user11336341
fonte