Stavo cercando di ottenere alcuni elenchi ordinati utilizzando OrderBy
all'interno di un ciclo foreach, ma per qualche motivo non stavano mantenendo il loro ordinamento al di fuori del ciclo. Ecco un po 'di codice e commenti per evidenziare quello che stava accadendo semplificata:Elenco <T> vs IEnumerable <T> in foreach
public class Parent
{
// Other properties...
public IList<Child> Children { get; set; }
}
public IEnumerable<Parent> DoStuff()
{
var result = DoOtherStuff() // Returns IEnumerable<Parent>
.OrderByDescending(SomePredicate)
.ThenBy(AnotherPredicate); // This sorting works as expected in the return value.
foreach (Parent parent in result)
{
parent.Children = parent.Children.OrderBy(YetAnotherPredicate).ToList();
// When I look at parent.Children here in the debugger, it's sorted properly.
}
return result;
// When I look at the return value, the Children are not sorted.
}
Tuttavia, quando invece assegnare result
come questo:
var result = DoOtherStuff()
.OrderByDescending(SomePredicate)
.ThenBy(AnotherPredicate)
.ToList(); // <-- Added ToList here
quindi il valore di ritorno ha i bambini smaltiti correttamente in ciascuna delle i genitori.
Qual è il comportamento di List<T>
rispetto a IEnumerable<T>
in un ciclo foreach?
Sembra esserci una sorta di differenza dal momento che trasformare i risultati in un elenco ha risolto i problemi con l'ordinamento nel ciclo foreach. Si si sente come il primo snippet di codice crea un iteratore che crea una copia di ogni elemento quando si esegue l'iterazione con foreach (e quindi le mie modifiche vengono applicate alla copia ma non l'oggetto originale in result
) mentre si utilizzava ToList()
l'enumeratore invece fornisce un puntatore.
Cosa sta succedendo qui?
@ user2864740: Capisco cosa intendi. Ho corretto la risposta. – Guffa
Questo è corretto .... Facilmente controllato inserendo un punto di interruzione in DoOtherStuff(). Ogni volta che si scorre l'enumeratore (IEnumerable) verrà chiamato DoOtherStuff. Call ToList() esegue l'enumeratore e posiziona il risultato in un elenco. L'iterazione nell'elenco non colpirà il punto di interruzione in DoOtherStuff() –
Mick
@Mick: il metodo 'DoOtherStuff' non verrà chiamato di nuovo, ma il codice all'interno del metodo potrebbe essere nuovamente eseguito. Il metodo restituisce un enumeratore e quando viene ricreato il codice che lo ha creato verrà eseguito nuovamente. – Guffa