IEnumerable vs List-czego używać? Jak działają?

Mam pewne wątpliwości, jak działają Enumeratory i LINQ. Rozważmy te dwa proste selekcje:

List<Animal> sel = (from animal in Animals 
                    join race in Species
                    on animal.SpeciesKey equals race.SpeciesKey
                    select animal).Distinct().ToList();

Lub

IEnumerable<Animal> sel = (from animal in Animals 
                           join race in Species
                           on animal.SpeciesKey equals race.SpeciesKey
                           select animal).Distinct();

Zmieniłem nazwy moich oryginalnych obiektów tak, że wygląda to na bardziej ogólny przykład. Samo zapytanie nie jest aż tak ważne. Chcę zapytać o to:

foreach (Animal animal in sel) { /*do stuff*/ }
  1. Zauważyłem, że jeśli używam IEnumerable, Kiedy debuguję i sprawdzam "sel", który w tym przypadku jest liczbą mnogą, ma kilka interesujących elementów: "inner", " outer", "innerKeySelector" i "outerKeySelector", te ostatnie 2 wydają się być delegatami. Członek " wewnętrzny "nie ma w sobie instancji" zwierzęcych", ale raczej instancji" gatunkowych", co było dla mnie bardzo dziwne. Element "zewnętrzny" zawiera instancje "zwierzęce". Zakładam, że obaj delegaci decydują, który z nich wchodzi, a co z niego wychodzi?

  2. Zauważyłem, że jeśli używam "Distinct", "inner" zawiera 6 elementów (jest to niepoprawne, ponieważ tylko 2 są odrębne), ale "outer" zawiera prawidłowe wartości. Ponownie, prawdopodobnie delegowane metody określają to, ale jest to trochę więcej niż Wiem o IEnumerable.

  3. Co najważniejsze, która z dwóch opcji jest najlepsza pod względem wydajności?

The evil List conversion via .ToList()?

A może używając kalkulatora bezpośrednio?

Jeśli możesz, proszę również wyjaśnić trochę lub rzucić kilka linków, które wyjaśniają to użycie IEnumerable.

Author: svick, 2010-09-02

9 answers

IEnumerable opisuje zachowanie, natomiast List jest implementacją tego zachowania. Kiedy używasz IEnumerable, dajesz kompilatorowi szansę odroczenia pracy na później, ewentualnie optymalizacji po drodze. Jeśli używasz metody ToList (), zmuszasz kompilator do natychmiastowej reify wyników.

Ilekroć "układam" wyrażenia LINQ, używam IEnumerable, ponieważ tylko określając zachowanie daję LINQ szansę odroczenia oceny i ewentualnej optymalizacji programu. Pamiętaj jak LINQ nie generuje SQL aby przeszukać bazę danych, dopóki jej nie wyliczysz? Rozważ to:

public IEnumerable<Animals> AllSpotted()
{
    return from a in Zoo.Animals
           where a.coat.HasSpots == true
           select a;
}

public IEnumerable<Animals> Feline(IEnumerable<Animals> sample)
{
    return from a in sample
           where a.race.Family == "Felidae"
           select a;
}

public IEnumerable<Animals> Canine(IEnumerable<Animals> sample)
{
    return from a in sample
           where a.race.Family == "Canidae"
           select a;
}

Teraz masz metodę, która wybiera początkową próbkę ("AllSpotted"), plus kilka filtrów. Więc teraz możesz to zrobić:

var Leopards = Feline(AllSpotted());
var Hyenas = Canine(AllSpotted());

Więc czy szybciej jest użyć listy nad IEnumerable? Tylko jeśli chcesz zapobiec wykonaniu zapytania więcej niż jeden raz. Ale czy ogólnie jest lepiej? W powyższym przykładzie, w pojedyncze zapytania SQL każde , A baza danych zwraca tylko wiersze, które są istotne. Ale jeśli zwróciliśmy listę z AllSpotted(), to może działać wolniej, ponieważ baza danych może zwrócić znacznie więcej danych, niż jest to rzeczywiście potrzebne, a my marnujemy cykle filtrowania w kliencie.

W programie może lepiej odłożyć konwersję zapytania na Listę do samego końca, więc jeśli mam wyliczać przez Leopardy i hieny więcej niż raz, zrobiłbym to:

List<Animals> Leopards = Feline(AllSpotted()).ToList();
List<Animals> Hyenas = Canine(AllSpotted()).ToList();
 609
Author: Chris Wenham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-22 20:53:27

Klasa implementująca IEnumerable pozwala na użycie składni foreach.

W zasadzie ma metodę, aby uzyskać następny element w kolekcji. Nie potrzebuje całej kolekcji, aby była w pamięci i nie wie, ile przedmiotów w niej jest, foreach po prostu dostaje następny przedmiot, dopóki się nie skończy.

Może to być bardzo przydatne w pewnych okolicznościach, na przykład w ogromnej tabeli bazy danych nie chcesz skopiować całej rzeczy do pamięci przed rozpoczęciem przetwarzania rzędy.

Teraz List implementuje IEnumerable, ale reprezentuje cały zbiór w pamięci. Jeśli masz IEnumerable i wywołujesz .ToList(), tworzysz nową listę z zawartością wyliczenia w pamięci.

Twoje wyrażenie linq zwraca wyliczenie i domyślnie wyrażenie jest wykonywane podczas iteracji za pomocą foreach. Instrukcja IEnumerable linq jest wykonywana podczas iteracji foreach, ale można ją zmusić do iteracji wcześniej używając .ToList().

Oto co ja średnia:

var things = 
    from item in BigDatabaseCall()
    where ....
    select item;

// this will iterate through the entire linq statement:
int count = things.Count();

// this will stop after iterating the first one, but will execute the linq again
bool hasAnyRecs = things.Any();

// this will execute the linq statement *again*
foreach( var thing in things ) ...

// this will copy the results to a list in memory
var list = things.ToList()

// this won't iterate through again, the list knows how many items are in it
int count2 = list.Count();

// this won't execute the linq statement - we have it copied to the list
foreach( var thing in list ) ...
 113
Author: Keith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-02 15:24:35

Jest bardzo dobry artykuł napisany przez: Claudio Bernasconi TechBlog tutaj: Kiedy używać IEnumerable, ICollection, IList i List

Oto kilka podstawowych punktów o scenariuszach i funkcjach:

Tutaj wpisz opis obrazkaTutaj wpisz opis obrazka

 104
Author: rubStackOverflow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-04 22:58:35

Najważniejszą rzeczą do zrealizowania jest to, że używając Linq, zapytanie nie jest natychmiast oceniane. Jest uruchamiany tylko jako część iteracji poprzez wynikowe IEnumerable<T> w foreach - to robią wszyscy dziwni delegaci.

Tak więc pierwszy przykład natychmiast ocenia zapytanie, wywołując ToList i umieszczając wyniki zapytania na liście.
Drugi przykład zwraca IEnumerable<T>, który zawiera wszystkie informacje potrzebne do późniejszego uruchomienia zapytania.

W kategoriach wydajność, odpowiedź brzmi to zależy . Jeśli chcesz, aby wyniki zostały ocenione na raz (powiedzmy, że mutujesz struktury, które pytasz później, lub jeśli nie chcesz, aby iteracja nad IEnumerable<T> trwała długo), Użyj listy. Else use an IEnumerable<T>. Domyślnym rozwiązaniem powinno być użycie oceny na żądanie w drugim przykładzie, ponieważ zwykle zużywa ona mniej pamięci, chyba że istnieje konkretny powód, aby zapisać wyniki na liście.

 64
Author: thecoop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-09 03:50:46

Nikt nie wspomniał o jednej istotnej różnicy, ironicznie odpowiedział na pytanie zamknięte jako powielone tego.

IEnumerable jest tylko do odczytu, a List Nie.

Zobacz praktyczna różnica między listą a liczbą

 62
Author: CAD bloke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:47

Zaletą IEnumerable jest odroczenie wykonania (zwykle z bazami danych). Zapytanie zostanie wykonane dopiero po zapętleniu danych. Jest to zapytanie czekające aż będzie potrzebne (aka leniwe Ładowanie).

Jeśli wywołasz ToList, zapytanie zostanie wykonane, lub "zmaterializowane", jak lubię mówić.

Są plusy i minusy obu. Jeśli zadzwonisz do ToList, możesz usunąć pewną tajemnicę, kiedy zapytanie zostanie wykonane. Jeśli trzymasz się liczby mnogiej, zyskujesz przewagę, że program nie wykonuje żadnej pracy, dopóki nie jest faktycznie wymagany.

 34
Author: Matt Sherman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-02 15:13:22

Jeśli chcesz tylko je wyliczyć, użyj IEnumerable.

Uważaj jednak, że zmiana oryginalnego zbioru jest niebezpieczną operacją - w tym przypadku będziesz chciał najpierw ToList. Spowoduje to utworzenie nowego elementu listy dla każdego elementu w pamięci, wyliczając IEnumerable i dlatego jest mniej wydajny, jeśli wyliczysz tylko raz - ale bezpieczniejszy i czasami metody List są przydatne (na przykład w dostępie losowym).

 15
Author: Daren Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-03 14:38:44

Podzielę się jednym błędnym pojęciem, w które wpadłem pewnego dnia:

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));


// updating existing list
names[0] = "ford";

// Guess what should be printed before continuing
print( startingWith_M.ToList() );
print( startingWith_F.ToList() );

Oczekiwany wynik

// I was expecting    
print( startingWith_M.ToList() ); // mercedes, mazda
print( startingWith_F.ToList() ); // fiat, ferrari

Rzeczywisty wynik

// what printed actualy   
print( startingWith_M.ToList() ); // mazda
print( startingWith_F.ToList() ); // ford, fiat, ferrari

Wyjaśnienie

Jak w przypadku innych odpowiedzi, ocena wyniku została odroczona do wywołania ToList lub podobnych metod wywołania, na przykład ToArray.

Więc mogę przepisać kod w tym przypadku jako:

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

// updating existing list
names[0] = "ford";

// before calling ToList directly
var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));

print( startingWith_M.ToList() );
print( startingWith_F.ToList() );

Zagraj w arround

Https://repl.it/E8Ki/0

 13
Author: amd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-23 16:26:14

Oprócz wszystkich odpowiedzi zamieszczonych powyżej, oto moje dwa grosze. Istnieje wiele innych typów niż Lista, które implementują liczbę takich ICollection, ArrayList itp. Jeśli więc jako parametr dowolnej metody mamy liczbę mnogą, możemy przekazać dowolne typy kolekcji do funkcji. Czyli możemy mieć metodę operowania na abstrakcji, a nie jakąś konkretną implementację.

 5
Author: Ananth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-22 23:43:18