Não é possível converter de um IEnumerable <T> para um ICollection <T>

94

Eu defini o seguinte:

public ICollection<Item> Items { get; set; }

Quando executo este código:

Items = _item.Get("001");

Recebo a seguinte mensagem:

Error   3   
Cannot implicitly convert type 
'System.Collections.Generic.IEnumerable<Storage.Models.Item>' to 
'System.Collections.Generic.ICollection<Storage.Models.Item>'. 
An explicit conversion exists (are you missing a cast?)

Alguém pode explicar o que estou fazendo de errado. Estou muito confuso sobre a diferença entre Enumerable, Collections e o uso de ToList ()

Informação adicionada

Posteriormente em meu código, tenho o seguinte:

for (var index = 0; index < Items.Count(); index++) 

Eu estaria bem em definir itens como um IEnumerable?

Samantha JT Star
fonte
3
Você pode fornecer mais informações sobre o tipo de _item e a assinatura de Get (string) (especificamente o tipo de retorno)?
Dr. Andrew Burnett-Thompson
Por que não mudar o tipo assim? public IEnumerable<Item> Items { get; set; }Você tem algum motivo especial para tê-lo como ICollection?
Shadow Wizard está vacinando
IEnumerable <T> Get (string pk);
Samantha JT Star

Respostas:

116

ICollection<T>herda de IEnumerable<T>então para atribuir o resultado de

IEnumerable<T> Get(string pk)

para um, ICollection<T>existem duas maneiras.

// 1. You know that the referenced object implements `ICollection<T>`,
//    so you can use a cast
ICollection<T> c = (ICollection<T>)Get("pk");

// 2. The returned object can be any `IEnumerable<T>`, so you need to 
//    enumerate it and put it into something implementing `ICollection<T>`. 
//    The easiest is to use `ToList()`:
ICollection<T> c = Get("pk").ToList();

A segunda opção é mais flexível, mas tem um impacto muito maior no desempenho. Outra opção é armazenar o resultado como um, a IEnumerable<T>menos que você precise da funcionalidade extra adicionada pela ICollection<T>interface.

Comentário Adicional de Desempenho

O loop que você tem

for (var index = 0; index < Items.Count(); index++)

funciona em um, IEnumerable<T>mas é ineficiente; cada chamada para Count()requer uma enumeração completa de todos os elementos. Use uma coleção e a Countpropriedade (sem parênteses) ou converta-a em um loop foreach:

foreach(var item in Items)
Anders Abel
fonte
1
Qual é a funcionalidade adicional adicionada por ICollection?
Samantha JT Star
7
ICollection<T>pode ser manipulado (consulte o documento para obter detalhes), enquanto um IEnumerable<T>só pode ser enumerado.
Anders Abel
Preciso ter algo onde possa executar diferentes consultas LINQ. Não preciso adicionar ou fazer nada parecido com a lista. Isso significa que eu estaria melhor com ICollection.
Samantha JT Star
LINQ funciona, IEnumerable<T>então IEnumerable<T>é o suficiente nesse caso.
Anders Abel
31

Você não pode converter diretamente de IEnumerable<T>para ICollection<T>. Você pode usar o ToListmétodo de IEnumerable<T>para convertê-lo emICollection<T>

someICollection = SomeIEnumerable.ToList();

Haris Hasan
fonte
Isso funciona para mim. Mas é uma boa ideia converter ou seria melhor usar IEnumerable como o tipo de Itens. Se eu usar o IEnumerable, estou fazendo com que possa realizar mais ações nos itens, se necessário posteriormente em meu programa?
Samantha JT Star
ICollection já implementa IEnumerable. Se você está preocupado com as ações disponíveis, acho que deveria ir para ICollection. É melhor se você puder ler a diferença real entre os dois e, em seguida, decidir o que realmente deseja usar
Haris Hasan
Estou tentando entender quais vantagens eu teria se usasse ToList (). Assim que tiver os dados, gostaria de executar consultas LINQ em itens.
Samantha JT Star
ToList () funcionará, mas como você declarou em seus comentários, Get () retorna uma ICollection, então, na verdade, você só precisa fazer uma conversão para ICollection <T>, ou melhor ainda, alterar a assinatura de retorno de Get para ICollection <T> . Usar ToList ou ToArray funcionará, mas também incorrerá em uma operação desnecessária de criação de memória / cópia
Dr. Andrew Burnett-Thompson
Você pode executar consultas LINQ em IEnumerable e ICollection. Vantagens ... uma que eu conheço agora é que Count ICollection mantém a contagem e o IEnumerable retorna o resultado calculando quantos itens ele tem. ICollection fornece método de remoção adicional que Ienumerables não oferece
Haris Hasan
1

Mais informações pendentes sobre a questão:

forneça mais informações sobre o tipo de item e a assinatura de Get

Duas coisas que você pode tentar são:

  • Para converter o valor de retorno de _item.Get to (ICollection)
  • em segundo lugar, use _item.Get ("001"). ToArray () ou _item.Get ("001"). ToList ()

Observe que o segundo causará um impacto no desempenho da cópia do array. Se a assinatura (tipo de retorno) de Get não for uma ICollection, a primeira não funcionará; se não for IEnumerable, a segunda não funcionará.


Seguindo seu esclarecimento à dúvida e nos comentários, eu pessoalmente declararia o tipo de retorno de _item.Get ("001") para ICollection. Isso significa que você não terá que fazer nenhuma conversão ou conversão (via ToList / ToArray) que envolveria uma operação desnecessária de criação / cópia.

// Leave this the same
public ICollection<Item> Items { get; set; }

// Change function signature here:
// As you mention Item uses the same underlying type, just return an ICollection<T>
public ICollection<Item> Get(string value); 

// Ideally here you want to call .Count on the collectoin, not .Count() on 
// IEnumerable, as this will result in a new Enumerator being created 
// per loop iteration
for (var index = 0; index < Items.Count(); index++) 

Cumprimentos,

Dr. Andrew Burnett-Thompson
fonte
1
A assinatura é: IEnumerable <T> Get (string pk); Itens é usado para manter os registros de itens que são devolvidos. Mais tarde, eu itero por meio deles para criar um relatório. Eu ficaria bem em usar qualquer coisa que funcione. Qual você acha que seria o melhor tipo de tipo de dados?
Samantha JT Star
1
Eu sugeriria alterar o tipo de dados de Itens para IEnumerable <T> para corresponder a Get ou alterar o tipo de retorno de Get para ICollection <T> para corresponder a Itens. Suponho que esses dois são do mesmo tipo subjacente apenas declarado de forma diferente? SE eles forem do mesmo tipo que você deseja evitar, usar ToList () ou ToArray (), pois isso executa uma alocação de memória desnecessária e uma cópia da matriz. Se eles não forem do mesmo tipo, você deve fazer alguma conversão
Dr. Andrew Burnett-Thompson
Eu adicionei à pergunta para mostrar onde estou usando Itens posteriormente. Você está correto ao dizer o mesmo tipo subjacente.
Samantha JT Star
Ok, então eu mudaria apenas o tipo de retorno de Get, já que você não precisa fazer nenhuma conversão (via ToList / ToArray, que é lento), ou casting. Espero que isso ajude :)
Dr. Andrew Burnett-Thompson