IEnumerable vs List – Wat te gebruiken? Hoe werken ze?

Ik heb wat twijfels over de werking van Enumerators en LINQ. Overweeg deze twee eenvoudige keuzes:

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

of

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

Ik heb de namen van mijn originele objecten gewijzigd, zodat dit een meer generiek voorbeeld lijkt. De query zelf is niet zo belangrijk. Wat ik wil vragen is dit:

foreach (Animal animal in sel) { /*do stuff*/ }
  1. Ik heb gemerkt dat als ik IEnumerablegebruik, wanneer ik “sel” debug en inspecteer, wat in dat geval de IEnumerable is, het een aantal interessante leden heeft: “inner”, “outer “, “innerKeySelector” en “outerKeySelector”, deze laatste 2 lijken afgevaardigden te zijn. Het “innerlijke” lid heeft geen “Animal”-instanties, maar eerder “Species”-instanties, wat heel vreemd voor mij was. Het “buitenste” lid bevat wel “Animal” instanties. Ik neem aan dat de twee afgevaardigden bepalen wat erin gaat en wat eruit gaat?

  2. Ik heb gemerkt dat als ik “Distinct” gebruik, de “innerlijke” 6 items bevat (dit is onjuist aangezien er slechts 2 Distinct zijn), maar de “outer” bevat wel de juiste waarden. Nogmaals, waarschijnlijk bepalen de gedelegeerde methoden dit, maar dit is iets meer dan ik weet over IEnumerable.

  3. Het belangrijkste is: welke van de twee opties is qua prestaties het beste?

De kwaadaardige lijstconversie via .ToList()?

Of misschien rechtstreeks de enumerator gebruiken?

Als je kunt, leg dan ook een beetje uit of gooi wat links die dit gebruik van IEnumerable uitleggen.


Antwoord 1, autoriteit 100%

IEnumerablebeschrijft gedrag, terwijl List een implementatie van dat gedrag is. Als je IEnumerablegebruikt, geef je de compiler de kans om het werk uit te stellen tot later, en eventueel te optimaliseren. Als je ToList() gebruikt, dwing je de compiler om de resultaten meteen opnieuw te verifiëren.

Telkens wanneer ik LINQ-expressies “stapel”, gebruik ik IEnumerable, omdat ik door alleen het gedrag te specificeren, LINQ de kans geef de evaluatie uit te stellen en mogelijk het programma te optimaliseren. Weet je nog hoe LINQ de SQL niet genereert om de database te doorzoeken totdat je deze opsomt? Overweeg dit:

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;
}

Je hebt nu een methode die een eerste sample selecteert (“AllSpotted”), plus enkele filters. Dus nu kun je dit doen:

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

Dus is het sneller om List te gebruiken in plaats van IEnumerable? Alleen als u wilt voorkomen dat een query meer dan één keer wordt uitgevoerd. Maar is het over het algemeen beter? Welnu, in het bovenstaande worden Leopards en Hyena’s elk omgezet in enkele SQL-query’s, en de database retourneert alleen de rijen die relevant zijn. Maar als we een lijst hadden geretourneerd van AllSpotted(), dan zou deze langzamer kunnen werken omdat de database veel meer gegevens zou kunnen retourneren dan daadwerkelijk nodig is, en we verspillen cycli met het filteren in de client.

In een programma is het misschien beter om het omzetten van uw zoekopdracht naar een lijst uit te stellen tot het einde, dus als ik meer dan eens door Leopards en Hyena’s ga opsommen, zou ik dit doen:

List<Animals> Leopards = Feline(AllSpotted()).ToList();
List<Animals> Hyenas = Canine(AllSpotted()).ToList();

Antwoord 2, autoriteit 29%

Er is een heel goed artikel geschreven door: Claudio Bernasconi’s TechBlog hier: Wanneer IEnumerable, ICollection, IList en List gebruiken

Hier enkele basispunten over scenario’s en functies:



Antwoord 3, autoriteit 18%

Met een klasse die IEnumerableimplementeert, kunt u de syntaxis foreachgebruiken.

In principe heeft het een methode om het volgende item in de verzameling te krijgen. Het hoeft niet de hele collectie in het geheugen te hebben en weet niet hoeveel items er in zitten, foreachblijft gewoon het volgende item krijgen totdat het op is.

Dit kan in bepaalde omstandigheden erg handig zijn, bijvoorbeeld in een enorme databasetabel wil je niet alles naar het geheugen kopiëren voordat je de rijen gaat verwerken.

Nu implementeert ListIEnumerable, maar vertegenwoordigt de volledige verzameling in het geheugen. Als je een IEnumerablehebt en je roept .ToList()aan, dan creëer je een nieuwe lijst met de inhoud van de opsomming in het geheugen.

Uw linq-expressie retourneert een opsomming, en standaard wordt de expressie uitgevoerd wanneer u itereert door de foreachte gebruiken. Een IEnumerablelinq-instructie wordt uitgevoerd wanneer u de foreachherhaalt, maar u kunt ervoor zorgen dat deze eerder wordt herhaald met behulp van .ToList().

Dit is wat ik bedoel:

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

Antwoord 4, autoriteit 13%

Niemand noemde één cruciaal verschil, ironisch genoeg beantwoord op een vraag die werd gesloten als een duplicaat hiervan.

IEnumerable is alleen-lezen en List niet.

Zie Praktisch verschil tussen List en IEnumerable


Antwoord 5, autoriteit 9%

Het belangrijkste om te beseffen is dat met Linq de query niet onmiddellijk wordt geëvalueerd. Het wordt alleen uitgevoerd als onderdeel van het doorlopen van de resulterende IEnumerable<T>in een foreach– dat is wat alle rare afgevaardigden doen.

Dus het eerste voorbeeld evalueert de query onmiddellijk door ToListaan te roepen en de queryresultaten in een lijst te plaatsen.
Het tweede voorbeeld retourneert een IEnumerable<T>die alle informatie bevat die nodig is om de query later uit te voeren.

In termen van prestaties is het antwoord het hangt ervan af. Als u de resultaten in één keer wilt evalueren (bijvoorbeeld, u muteert de structuren die u later opvraagt, of als u niet wilt dat de iteratie over de IEnumerable<T>een lange tijd) gebruik een lijst. Gebruik anders een IEnumerable<T>. De standaard zou moeten zijn om de evaluatie op aanvraag in het tweede voorbeeld te gebruiken, omdat dat over het algemeen minder geheugen gebruikt, tenzij er een specifieke reden is om de resultaten in een lijst op te slaan.


Antwoord 6, autoriteit 5%

Het voordeel van IEnumerable is uitgestelde uitvoering (meestal met databases). De query wordt pas uitgevoerd als u de gegevens daadwerkelijk doorloopt. Het is een zoekopdracht die wacht tot het nodig is (ook wel lui laden genoemd).

Als je ToList aanroept, wordt de query uitgevoerd, of “gematerialiseerd”, zoals ik graag zeg.

Er zijn voor- en nadelen aan beide. Als u ToList aanroept, kunt u enig mysterie verwijderen over wanneer de query wordt uitgevoerd. Als je je aan IEnumerable houdt, heb je het voordeel dat het programma niets doet totdat het echt nodig is.


Antwoord 7, autoriteit 4%

Ik zal een verkeerd gebruikt concept delen waar ik ooit in terecht ben gekomen:

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

Verwacht resultaat

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

Eigenlijk resultaat

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

Uitleg

Zoals bij andere antwoorden, werd de evaluatie van het resultaat uitgesteld tot het aanroepen van ToListof vergelijkbare aanroepmethoden, bijvoorbeeld ToArray.

Dus ik kan de code in dit geval herschrijven als:

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

Play Arround

https://repl.it/E8KI/0


Antwoord 8, Autoriteit 2%

Als alles wat u wilt doen, gebruikt u deze, gebruik de IEnumerable.

Pas op, dat het veranderen van de oorspronkelijke collectie wordt opgesomd, is een gevaarlijke operatie – in dit geval wilt u ToListeerst. Dit maakt een nieuw lijstelement voor elk element in het geheugen, en vermaak de IEnumerableen is dus minder performant als u slechts eenmaal opsommingt – maar veiliger en soms de ListMethods zijn handig (bijvoorbeeld in willekeurige toegang).


Antwoord 9

Naast alle hierboven gepubliceerde antwoorden, is hier mijn twee cent. Er zijn veel andere typen anders dan lijst die beurt beleefd, dergelijke icollectie, arrayclist etc., dus als we anneilabel hebben als parameter van elke methode, kunnen we eventuele verzamelingstypen doorgeven aan de functie. Dwz, we kunnen methode hebben om op abstractie te werken, geen specifieke implementatie.


Antwoord 10

Er zijn veel gevallen (zoals een oneindige lijst of een zeer grote lijst) waar IEnumereerbaar niet kan worden getransformeerd naar een lijst. De meest voor de hand liggende voorbeelden zijn alle priemgetallen, alle gebruikers van Facebook met hun gegevens, of alle items op eBay.

Het verschil is dat “Lijst” -objecten worden opgeslagen “hier en op dit moment”, terwijl “Ienumerable” objecten “slechts één tegelijk” werken. Dus als ik alle items op eBay doormaak, zou één voor een tijd iets zijn, zelfs een kleine computer kan aankunnen, maar “. Tolist ()” zou me zeker uit het geheugen leiden, ongeacht hoe groot mijn computer was. Geen computer kan op zichzelf een zo’n enorme hoeveelheid gegevens bevatten en aan.

[Bewerken] – Onnodig te zeggen – het is niet “of dit of dat”. vaak zou het zinvol zijn om zowel een lijst als een IEnumerable in dezelfde klasse te gebruiken. Geen enkele computer ter wereld zou alle priemgetallen kunnen opsommen, omdat dit per definitie een oneindige hoeveelheid geheugen zou vergen. Maar je zou gemakkelijk een class PrimeContainerkunnen bedenken die een . bevat
IEnumerable<long> primes, die om voor de hand liggende redenen ook een SortedList<long> _primes. alle priemgetallen tot nu toe berekend. het volgende te controleren priemgetal zou alleen worden uitgevoerd tegen de bestaande priemgetallen (tot aan de vierkantswortel). Op die manier krijg je beide – priemgetallen één voor één (IEnumerable) en een goede lijst van “priemgetallen tot nu toe”, wat een redelijk goede benadering is van de hele (oneindige) lijst.

Other episodes