Nie można przekonwertować z IEnumerable <T> na ICollection <T>


91

Zdefiniowałem co następuje:

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

Kiedy uruchamiam ten kod:

Items = _item.Get("001");

Otrzymuję następujący komunikat:

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?)

Czy ktoś może wyjaśnić, co robię źle. Jestem bardzo zdezorientowany różnicą między Enumerable, Collections i użyciem ToList ()

Dodatkowe informacje

Później w moim kodzie mam:

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

Czy byłbym w stanie zdefiniować elementy jako IEnumerable?


3
Czy możesz podać więcej informacji na temat typu _item i sygnatury Get (string) (w szczególności zwracanego typu)?
Dr Andrew Burnett-Thompson

Dlaczego nie zmienić tego typu? public IEnumerable<Item> Items { get; set; }Czy masz jakiś szczególny powód, aby mieć to jako ICollection?
Shadow Wizard is Ear For You

IEnumerable <T> Get (string pk);
Samantha JT Star

Odpowiedzi:


113

ICollection<T>dziedziczy z IEnumerable<T>tak, aby przypisać wynik

IEnumerable<T> Get(string pk)

do ICollection<T>są dwa sposoby.

// 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();

Druga opcja jest bardziej elastyczna, ale ma znacznie większy wpływ na wydajność. Inną opcją jest zapisanie wyniku jako, IEnumerable<T>chyba że potrzebujesz dodatkowej funkcjonalności dodanej przez ICollection<T>interfejs.

Dodatkowy komentarz dotyczący wydajności

Pętla, którą masz

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

działa, IEnumerable<T>ale jest nieefektywny; każde wywołanie Count()wymaga pełnego wyliczenia wszystkich elementów. Użyj kolekcji i Countwłaściwości (bez nawiasów) lub przekonwertuj je na pętlę foreach:

foreach(var item in Items)

1
Jaka jest dodatkowa funkcjonalność dodana przez ICollection?
Samantha JT Star

7
ICollection<T>można nim manipulować ( szczegółowe informacje można znaleźć w dokumencie ), natomiast IEnumerable<T>można je tylko wyliczyć.
Anders Abel

Muszę mieć coś, na czym mogę uruchamiać różne zapytania LINQ. Nie muszę dodawać ani robić niczego takiego na liście. Czy to oznacza, że ​​byłoby mi lepiej z ICollection.
Samantha JT Star

LINQ działa, IEnumerable<T>więc IEnumerable<T>w tym przypadku wystarczy.
Anders Abel

31

Nie możesz dokonać konwersji bezpośrednio z IEnumerable<T>na ICollection<T>. Możesz użyć ToListmetody, IEnumerable<T>aby przekonwertować go naICollection<T>

someICollection = SomeIEnumerable.ToList();


To działa dla mnie. Ale czy konwersja jest dobrym pomysłem, czy lepiej byłoby użyć IEnumerable jako typu elementów. Jeśli używam IEnumerable, czy robię to, aby móc wykonać więcej akcji na elementach, jeśli zajdzie taka potrzeba w dalszej części mojego programu?
Samantha JT Star

ICollection już implementuje IEnumerable. Jeśli martwisz się dostępnymi działaniami, myślę, że powinieneś wybrać ICollection. Lepiej jest, jeśli możesz przeczytać rzeczywistą różnicę między nimi, a następnie zdecydować, czego tak naprawdę chcesz użyć
Haris Hasan,

Próbuję zrozumieć, jakie korzyści miałbym, gdybym użył ToList (). Gdy mam dane, chciałbym uruchamiać zapytania LINQ względem elementów.
Samantha JT Star

ToList () będzie działać, ale jak już napisałeś w swoich komentarzach Get () zwraca ICollection, więc tak naprawdę wystarczy wykonać rzutowanie na ICollection <T> lub jeszcze lepiej, zmienić sygnaturę zwrotną Get to ICollection <T> . Używanie ToList lub ToArray będzie działać, ale spowoduje również niepotrzebną operację tworzenia pamięci / kopiowania
Dr Andrew Burnett-Thompson

Zapytania LINQ można uruchamiać zarówno dla IEnumerable, jak i ICollection. Zalety ... jeden, który teraz znam, to Count ICollection utrzymuje liczbę, a IEnumerable zwraca wynik, obliczając liczbę elementów, które ma. ICollection zapewnia dodatkową metodę usuwania, której Ienumerables nie robi
Haris Hasan

1

W oczekiwaniu na więcej informacji na pytanie:

proszę podać więcej informacji na temat rodzaju przedmiotu i podpisu Get

Możesz spróbować dwóch rzeczy:

  • Aby rzutować wartość zwracaną przez _item. Get to (ICollection)
  • po drugie, aby użyć _item.Get („001”). ToArray () lub _item.Get („001”). ToList ()

Należy pamiętać, że drugi spowoduje spadek wydajności kopii tablicy. Jeśli podpis (typ zwracany) Get nie jest ICollection, to pierwsza nie będzie działać, jeśli nie jest IEnumerable, to druga nie będzie działać.


Po wyjaśnieniu pytania i komentarzach osobiście zadeklarowałbym zwracany typ _item.Get („001”) do ICollection. Oznacza to, że nie będziesz musiał wykonywać żadnego rzutowania ani konwersji (przez ToList / ToArray), co wymagałoby niepotrzebnej operacji tworzenia / kopiowania.

// 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++) 

Z poważaniem,


1
Podpis to: IEnumerable <T> Get (string pk); Towary są używane do przechowywania zwracanych rekordów pozycji. Później powtarzam je, aby utworzyć raport. Byłbym w porządku, jeśli użyłbym wszystkiego, co działa. Jak myślisz, jaki byłby najlepszy typ danych?
Samantha JT Star

1
Sugerowałbym zmianę typu danych elementów na IEnumerable <T>, aby dopasować Get, lub zmianę typu zwracanego Get to ICollection <T>, aby dopasować elementy. Zakładam, że te dwa są tego samego typu podstawowego, właśnie zadeklarowanego inaczej? JEŚLI są tego samego typu, których chcesz uniknąć przy użyciu ToList () lub ToArray (), ponieważ wykonuje to niepotrzebną alokację pamięci i kopiowanie tablicy. Jeśli nie są tego samego typu, musisz dokonać konwersji
Dr Andrew Burnett-Thompson

Dodałem do pytania, aby pokazać, gdzie później korzystam z elementów. Masz rację, mówiąc o tym samym podstawowym typie.
Samantha JT Star

Ok, w takim razie zmieniłbym tylko zwracany typ Get, ponieważ wtedy nie trzeba wykonywać żadnej konwersji (przez ToList / ToArray, która jest wolna) ani rzutowania. Mam nadzieję, że to pomoże :)
Dr. Andrew Burnett-Thompson
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.