c# - net - ienumerable method




IEnumerable vs List-Che cosa usare? Come funzionano? (6)

Ho alcuni dubbi su come funzionano gli Enumerator e LINQ. Considera questi due semplici aspetti:

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

o

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

Ho cambiato i nomi dei miei oggetti originali in modo che questo assomigli ad un esempio più generico. La query stessa non è così importante. Quello che voglio chiedere è questo:

foreach (Animal animal in sel) { /*do stuff*/ }
  1. Ho notato che se utilizzo IEnumerable , quando eseguo il debug e controllo "sel", che in quel caso è l'IEnumerable, ha alcuni membri interessanti: "inner", "outer", "innerKeySelector" e "outerKeySelector", questi ultimi 2 sembrano essere delegati. Il membro "interno" non ha istanze "Animali" in esso, ma piuttosto istanze "Specie", che era molto strano per me. Il membro "esterno" contiene istanze "Animali". Presumo che i due delegati determinino quali elementi entrano e cosa ne esce?

  2. Ho notato che se uso "Distinct", "inner" contiene 6 elementi (questo non è corretto poiché solo 2 sono Distinct), ma "outer" contiene i valori corretti. Ancora una volta, probabilmente i metodi delegati determinano questo, ma questo è un po 'più di quanto so su IEnumerable.

  3. Ancora più importante, quale delle due opzioni è la migliore per quanto riguarda le prestazioni?

La conversione Elenco male tramite .ToList() ?

O magari usando direttamente l'enumeratore?

Se puoi, per favore spiega anche un po 'o getta alcuni link che spiegano questo uso di IEnumerable.



Condividerò un concetto abusato in cui sono caduto in un giorno:

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

Risultato atteso

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

Risultato attuale

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

Spiegazione

Come per le altre risposte, la valutazione del risultato è stata posticipata fino a chiamare ToList o metodi di chiamata simili, ad esempio ToArray .

Quindi posso riscrivere il codice in questo caso come:

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

Gioca vicino

https://repl.it/E8Ki/0


La cosa più importante da capire è che, usando Linq, la query non viene immediatamente valutata. Viene eseguito solo come parte dell'iterazione attraverso il risultante IEnumerable<T> in un foreach - questo è ciò che fanno tutti i delegati strani.

Pertanto, il primo esempio valuta immediatamente la query chiamando ToList e inserendo i risultati della query in un elenco.
Il secondo esempio restituisce un IEnumerable<T> che contiene tutte le informazioni necessarie per eseguire la query in seguito.

In termini di prestazioni, la risposta è che dipende . Se hai bisogno che i risultati vengano valutati contemporaneamente (per esempio, stai mutando le strutture che stai interrogando in seguito, o se non vuoi che l'iterazione su IEnumerable<T> impieghi molto tempo) usi un elenco . Altrimenti usa un oggetto IEnumerable<T> . L'impostazione predefinita dovrebbe essere quella di utilizzare la valutazione su richiesta nel secondo esempio, in quanto generalmente utilizza meno memoria, a meno che non vi sia un motivo specifico per archiviare i risultati in un elenco.


Nessuno ha menzionato una differenza cruciale, ha ironicamente risposto a una domanda chiusa come duplicata di questo.

IEnumerable è di sola lettura e List non lo è.

Vedi la differenza pratica tra List e IEnumerable


Se tutto quello che vuoi fare è elencarli, usa IEnumerable .

Attenzione, tuttavia, che la modifica della raccolta originale elencata è un'operazione pericolosa: in questo caso, per prima ToList necessario selezionare ToList . Questo creerà un nuovo elemento di lista per ogni elemento in memoria, enumerando l'oggetto IEnumerable ed è quindi meno performante se si enumera solo una volta, ma più sicuro ea volte i metodi List sono a portata di mano (ad esempio in accesso casuale).


Una classe che implementa IEnumerable consente di utilizzare la sintassi foreach .

Fondamentalmente ha un metodo per ottenere il prossimo oggetto nella collezione. Non ha bisogno che tutta la collezione sia in memoria e non sa quanti oggetti ci sono dentro, foreach continua a ricevere il prossimo oggetto fino a quando non finisce.

Questo può essere molto utile in determinate circostanze, ad esempio in una tabella di database enorme non si desidera copiare l'intera cosa in memoria prima di iniziare l'elaborazione delle righe.

Ora List implementa IEnumerable , ma rappresenta l'intera raccolta in memoria. Se si dispone di un oggetto IEnumerable e si chiama .ToList() si crea una nuova lista con i contenuti dell'enumerazione in memoria.

L'espressione linq restituisce un'enumerazione e, per impostazione predefinita, l'espressione viene eseguita quando si esegue l'iterazione tramite il foreach . Un'istruzione IEumerable linq viene eseguita quando si itera il foreach , ma è possibile forzarlo per iterare prima usando .ToList() .

Ecco cosa intendo:

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




ienumerable