2014-08-29 12 views
6

Powiedzmy, że mamy list.Where(p=>p.Number > n).Select(p=> p.Name).Where(n=> n.StartsWith(a)).ToList(); czy zostanie uruchomiony algorytm jednoprzebiegowy, czy będzie to 3 przejścia?Czy LINQ próbuje rozwiązać problemy za jednym razem?

+0

Załóżmy, że mamy egzamin państwowy i studenci są proszeni o rozwiązanie prostego zadania algorytmicznego, które można rozwiązać za pomocą C# LINQ w jednym ładnym, prostym zapytaniu. Jednak jeśli nie uda im się udowodnić na przebiegach w jednym przejściu, zostanie uznane za nieoptymalne rozwiązanie, a uczniowie otrzymają złe oceny. – Rella

+0

Zauważ, że istnieje podobne pytanie dotyczące Linq2SQL: czy otrzymane zapytanie zostanie wykonane przy użyciu pojedynczej instrukcji 'SELECT'? W przypadku prostych zapytań, takich jak przykład, jeśli można je przekonwertować na zapytanie SQL, odpowiedź brzmi: tak. Ale niektóre [złożone] (http://stackoverflow.com/q/22816591/256431) [zapytania] (http://stackoverflow.com/q/12264751/256431) nie robią tego i nie wiem, czy każda gwarancja jest udzielana. –

Odpowiedz

6

Będzie budować listę w jednym przebiegu, ze względu na sposób, w jaki dane LINQ strumieni.

Na przykład, weźmy to:

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

To samo w sobie nie wygląda na dowolny z elementów listy. Zamiast tego zapamiętuje listę, na którą patrzysz, a kiedy zaczynasz iterować ponad query, za każdym razem, gdy poprosisz o następny element, będzie on sprawdzał elementy listy po kolei, dopóki nie znajdzie dopasowania - następnie przestanie. Na przykład:

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 
} 

Każda z operacji działa w ten sposób - tak przez czas masz otrzymała:

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

... jeśli poprosisz o pierwszej pozycji w ciągu query, to będzie łańcuch kopii zapasowej (więc ostatni Where zapyta o wynik Select, który poprosi o wynik pierwszego Where, który poprosi o listę) i będzie kontynuował aż do uzyskania wyniku. Wtedy, kiedy prosić o następnym punkcie, że poprosi wynik Select do następnego elementu, itp

ToList buduje List<T> ze wszystkich przedmiotów w jego źródła, natychmiast - to chętny w tym sensie (zamiast innych operatorów, którzy są tutaj leniwi). Ale sama oryginalna lista będzie wciąż powtarzana tylko raz.

Dla część więcej szczegółów o tym, jak działa LINQ to Objects - w tym przykładowa implementacja - możesz przeczytać moją Edulinq blog series.

8

list byłby iterowany tylko raz w tym kodzie, a nie 3 razy.

Oczywiście, jeśli chcesz sprawdzić, czy jakikolwiek arbitralny kwerendy iteracje po źródłach wielokrotnie jest to dość łatwe do przetestować eksperymentalnie, po prostu utwórz IEnumerable że zgłasza wyjątek podczas próby iteracyjne to wiele razy:

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

teraz można po prostu napisać:

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

Jeśli kod zgłasza wyjątek, próbowałeś iteracyjne listę o leżących u podstaw wielokrotnie. Jeśli nie, nie zrobiłeś tego.

Powiązane problemy