IEnumerable vs List

0

Witam
Jestem początkującym w C#, napotkałem problem na interfejsie IEnumerable<T>. Nie rozumiem czym się różni w tym przypadku od List<T>. Podaję przykład z książki, którego nie rozumiem:

private List<Weapon> inventory = new List<Weapon>();//Weapon - klasa z bronią
public IEnumerable<string> Weapons {
  get {
    List<string> names = new List<string>();
    foreach (Weapon weapon in inventory)
      names.Add(weapon.Name);
    return names;
  }
} 

Proszę o pomoc.

2

IEnumerable<T> nie dostarcza metod do modyfikacji kolekcji w miejscu. Przez to, jak zwracasz IEnumerable<T> to masz pewnosc, ze nikt nie zmodyfikuje wewnetrznej kolekcji. http://ideone.com/hl65FL

Zreszta, ten kod jest ogolnie bezsensu, powinnien wygladac mniej-wiecej tak:

get { return inventory.Select(weapon => weapon.Name); }
0

Dzięki za odpowiedź! Teraz rozumiem ;) Czy jest jakieś źródło tudzież książka, z której warto się uczyć? Czy tylko z msdn?

0

Przez to, jak zwracasz IEnumerable<T> to masz pewnosc, ze nikt nie zmodyfikuje wewnetrznej kolekcji. http://ideone.com/hl65FL

Nieprawda.
http://ideone.com/DfOMNM

Spróbuję to trafniej ująć: zwracając IEnumerable<T> komunikujesz to, że nie chcesz by ktoś zmodyfikował wewnętrzną kolekcję.

1

IEnumerable<T> ma taką nieprzyjemną właściwość, że można po nim tylko iterować, nie wiadomo ile ma elementów (Count() żeby zwrócić ten wynik musi przeiterować po wszystkich elementach), generalnie nic nie wiadomo do momentu enumeracji elementów.

@n0name_l, @Rev: moim zdaniem do zakomunikowania, że kolekcja jest readonly, służy... kolekcja readonly (ReadOnlyCollection<T>).

0

Ja bym jeszcze dodał że List dziedziczy IEnumerable (oraz ICollection) i zwyczajnie dodaje "swoje" metody": Add, Clear, Contains, Insert etc.

0

@kry5

Klasa generyczna List<T> jest jedną z implementacji interfejsu IEnumerable<T>. A sam interfejs IEnumerable<T> jest typem używanym do tworzenia enumeratorów i tzw "leniwych kolekcji" (kolekcje do których elementów sięgamy tylko w momencie faktycznego zapotrzebowania) przy użyciu m.in składni 'foreach' oraz 'yield return'.

@ŁF ma rację... jeśli chcesz kolekcji tylko do odczytu wtedy użyj ReadOnlyCollection<T>. Użycie do tego celu interfejsu IEnumerable<T> jest kompletną bzdurą ponieważ zawsze możesz rzutować ten typ na właściwy typ kolekcji...

0

jest kompletną bzdurą ponieważ zawsze możesz rzutować ten typ na właściwy typ kolekcji

Imo, jedyna bzdura jest zakladanie, ze uzytkownik klasy to najgorszy szkodnik i chronienie sie na sile przed tym. IEnumerable<> jest zdecydowanie wystarczajaca informacja, a argument, ze ktos moze sobie rzutowac i zmieniac jednoczesnie mowiac, ze ReadOnlyCollection<> nie mozna jest po prostu smieszny. http://ideone.com/xD5K3v

4

Ja trochę nie rozumiem skąd ta dyskusja o różnicach między pralką, a lodówką. Przecież one są oczywiste.

Jeśli piszemy metodę, której celem jest przejście po jakiejś przekazanej w argumencie kolekcji, to jako typ przyjmowany deklarujemy IEnumerable<T>. Jeśli zaś chcemy, aby nie można było modyfikować kolekcji jako takiej, to użyjemy IReadOnlyCollection<T>. Jeśli dodatkowo chcemy mieć dostęp do właściwości Count albo odwoływać się do elementów po indeksie, to deklarujemy jako IReadOnlyList<T>. A jeśli potrzebujemy dodawania bądź usuwania elementów z kolekcji, to jako typ przyjmowany deklarujemy IList<T>. Interfejs IList<T>, nie klasę List<T>!
Właściwie analogiczne zasady dotyczą zasad definiowania kolekcji w interfejsie.
I generalnie w przypadku dowolnej klasy dobrze implementować jej właściwości/pola jako ReadOnlyCollection<T>/ReadOnlyList<T> (nawet jeśli mają być implementacjami właściwości typu IEnumerable<T>), tak aby to ona odpowiadała za modyfikację swoich kolekcji, a inne klasy nie miały takiej możliwości.

2
n0name_l napisał(a):

Imo, jedyna bzdura jest zakladanie, ze uzytkownik klasy to najgorszy szkodnik i chronienie sie na sile przed tym. IEnumerable<> jest zdecydowanie wystarczajaca informacja, a argument, ze ktos moze sobie rzutowac i zmieniac jednoczesnie mowiac, ze ReadOnlyCollection<> nie mozna jest po prostu smieszny. http://ideone.com/xD5K3v

Nie bardziej śmieszny niż Twój przykład z dostępem do "chronionego" typu przez refleksję... przez którą można zrobić (prawie) wszystko. I co to ma udowadniać? Typów powinno się używać adekwatnie do sytuacji i pomyłką jest traktowanie IEnumerable<T> jako alternatywy "tylko do odczytu" dla listy tylko dlatego, że udostępnia mniejszą liczbę metod. Niby można ale nie wykorzystuje się jego potencjału i naraża na nieoptymalne wykorzystanie (wielokrotne wyliczanie). Ten interfejs ma zupełnie inne zastosowanie (enumeartory o których pisałem) i powinien być używany raczej w tym kontekscie:

Widząc prawidłowo zaimplementowaną metodę o sygnaturze:

public IEnumerable<T> GetSomeElements()
{
}

Wiem nie tylko co robi, ale i jak to robi...

Spójż na przykład który podał @kry5, widząc właściwość o takim typie ('Weapons') spodziewałbym się, że dostanę tylko tyle elementów o ile faktycznie zapytam a tam w momencie odwołania jest tworzona i zwracana cała kolekcja elementów... Twój przykład już jest dużo lepszy bo samo Select() zwraca IEnumerable<T> i w takim kontekście jego użycie ma sens.

@somekind dokładnie

0

Dzięki za wszystkie odpowiedzi ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1