2014-08-29 9 views
6

Sagen wir haben list.Where(p=>p.Number > n).Select(p=> p.Name).Where(n=> n.StartsWith(a)).ToList(); wird dies ein Durchlaufalgorithmus oder es wird 3 Pässe sein?Versucht LINQ, Dinge in einem Durchgang zu lösen?

+0

Angenommen, wir haben eine Staatsprüfung, und die Schüler werden gebeten, eine einfache algorithmische Aufgabe zu lösen, die über C# LINQ in einer netten, einfachen Abfrage gelöst werden kann. Wenn sie jedoch in einem Durchgang nicht in Läufen beweisen können, wird dies als eine nicht optimale Lösung angesehen und die Schüler bekommen schlechte Noten. – Rella

+0

Beachten Sie, dass es eine ähnliche Frage zu Linq2SQL gibt: Wird die resultierende Abfrage mit einer einzelnen 'SELECT' Anweisung ausgeführt? Für einfache Abfragen wie Ihr Beispiel, wenn sie in eine SQL-Abfrage konvertiert werden können, ist die Antwort ja. Aber einige [komplex] (http://stackoverflow.com/q/22816591/256431) [Anfragen] (http://Stackoverflow.com/q/12264751/256431) tun dies nicht, und ich weiß nicht, ob jede Garantie ist gegeben. –

Antwort

6

Es wird die Liste in einem Durchgang aufbauen, aufgrund der Art, wie LINQ die Daten streamt.

Nehmen wir zum Beispiel diese:

var query = list.Where(p => p.Number > n); 

Das ist an sich nicht sieht jede der Elemente der Liste. Stattdessen merkt es sich die Liste, die Sie betrachten, und wenn Sie über query iterieren, werden bei jeder Abfrage nach dem nächsten Element die Elemente der Liste nacheinander überprüft, bis eine Übereinstimmung gefunden wird. Zum Beispiel:

using (var iterator = query.GetEnumerator()) 
{ 
    iterator.MoveNext(); // This will look for the first match 
    Console.WriteLine(iterator.Current); 

    iterator.MoveNext(); // This will continue from just after the first match 
} 

Jede der Operationen funktioniert so - so durch die Zeit, die Sie haben:

var query = list.Where(...) 
       .Select(...) 
       .Where(...); 

..., wenn Sie für den ersten Punkt fragen innerhalb query, wird es Kette Backup (also die letzte Where wird das Ergebnis von Select fragen, die das Ergebnis der ersten Where fragen wird, die die Liste fragen wird) und weitermachen, bis es ein Ergebnis hat. Dann, wenn Sie für das nächste Element fragen, dass das Ergebnis der Select für das nächste Element fragen usw.

ToList up baut ein List<T> von allen Elementen in der Quelle, sofort - es ist eifrig in diesem Sinne (eher als die anderen Betreiber hier, die sind faul). Die ursprüngliche Liste selbst wird jedoch nur einmal durchlaufen.

Für eine viel weitere Einzelheiten über LINQ to Objects funktioniert - einschließlich einer Beispielimplementierung - Sie könnten meine Edulinq blog series lesen möchten.

8

list würde nur einmal in diesem Code, nicht 3 Mal iteriert werden.

Natürlich, wenn Sie, wenn eine beliebige Abfrage testen wollen die Quelle mehrmals iteriert es einfach genug ist experimentell zu testen, nur ein IEnumerable erstellen, die eine Ausnahme auslöst, wenn Sie versuchen, es mehrmals zu wiederholen:

public static IEnumerable<T> ThereCanBeOnlyOne<T>(this IEnumerable<T> source) 
{ 
    return new SingleEnumerable<T>(source); 
} 

private class SingleEnumerable<T> : IEnumerable<T> 
{ 
    private bool hasRun = false; 
    private IEnumerable<T> wrapped; 
    public SingleEnumerable(IEnumerable<T> wrapped) 
    { 
     this.wrapped = wrapped; 
    } 
    public IEnumerator<T> GetEnumerator() 
    { 
     if (hasRun) 
      throw new InvalidOperationException(
       "Sequence cannot be enumerated multilpe times"); 
     else 
     { 
      hasRun = true; 
      return wrapped.GetEnumerator(); 
     } 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

Jetzt können Sie einfach schreiben:

list.ThereCanBeOnlyOne() 
    .Where(p=>p.Number > n) 
    .Select(p=> p.Name) 
    .Where(n=> n.StartsWith(a)) 
    .ToList(); 

Wenn der Code eine Ausnahme auslöst, können Sie die zugrunde liegende Liste mehrere Male zu wiederholen versucht. Wenn nicht, hast du es nicht getan.

Verwandte Themen