linq
Tryby wykonywania metody - natychmiastowe, odroczone przesyłanie strumieniowe, odroczone nieprzesyłanie strumieniowe
Szukaj…
Odroczenie wykonania a natychmiastowe wykonanie
Niektóre metody LINQ zwracają obiekt zapytania. Ten obiekt nie przechowuje wyników zapytania; zamiast tego zawiera wszystkie informacje potrzebne do wygenerowania tych wyników:
var list = new List<int>() {1, 2, 3, 4, 5};
var query = list.Select(x => {
Console.Write($"{x} ");
return x;
});
Zapytanie zawiera wywołanie Console.Write
, ale nic nie zostało przesłane do konsoli. Jest tak, ponieważ zapytanie nie zostało jeszcze wykonane, a zatem funkcja przekazana do Select
nigdy nie została oceniona. Nazywa się to odroczeniem wykonania - wykonanie zapytania jest opóźnione do pewnego momentu.
Inne metody LINQ wymuszają natychmiastowe wykonanie zapytania; te metody wykonują zapytanie i generują jego wartości:
var newList = query.ToList();
W tym momencie funkcja przekazana do Select
zostanie oceniona dla każdej wartości z oryginalnej listy, a następujące informacje zostaną przesłane do konsoli:
1 2 3 4 5
Zasadniczo metody LINQ, które zwracają pojedynczą wartość (np. Max
lub Count
) lub zwracają obiekt, który faktycznie przechowuje wartości (takie jak ToList
lub ToDictionary
), są wykonywane natychmiast.
Metody zwracające IEnumerable<T>
lub IQueryable<T>
zwracają obiekt zapytania i pozwalają na odroczenie wykonania do późniejszego momentu.
To, czy określona metoda LINQ wymusza natychmiastowe wykonanie zapytania, można znaleźć w witrynie MSDN - C # lub VB.NET .
Tryb strumieniowy (leniwa ocena) vs tryb non-streaming (szybka ocena)
Spośród metod LINQ, które wykorzystują odroczone wykonywanie, niektóre wymagają oceny pojedynczej wartości na raz. Następujący kod:
var lst = new List<int>() {3, 5, 1, 2};
var streamingQuery = lst.Select(x => {
Console.WriteLine(x);
return x;
});
foreach (var i in streamingQuery) {
Console.WriteLine($"foreach iteration value: {i}");
}
wyświetli:
3)
każda wartość iteracji: 3
5
każda wartość iteracji: 5
1
każda wartość iteracji: 1
2)
każda wartość iteracji: 2
ponieważ funkcja przekazana do Select
jest oceniana przy każdej iteracji foreach
. Jest to znane jako tryb przesyłania strumieniowego lub leniwa ocena .
Inne metody LINQ - operatory sortujące i grupujące - wymagają oceny wszystkich wartości, zanim będą mogły zwrócić jakąkolwiek wartość:
var nonStreamingQuery = lst.OrderBy(x => {
Console.WriteLine(x);
return x;
});
foreach (var i in nonStreamingQuery) {
Console.WriteLine($"foreach iteration value: {i}");
}
wyświetli:
3)
5
1
2)
każda wartość iteracji: 1
każda wartość iteracji: 2
każda wartość iteracji: 3
każda wartość iteracji: 5
W tym przypadku, ponieważ wartości muszą być generowane dla foreach
w porządku rosnącym, wszystkie elementy muszą najpierw zostać ocenione, aby ustalić, która jest najmniejsza, a która następna najmniejsza i tak dalej. Jest to znane jako tryb bez przesyłania strumieniowego lub szybka ocena .
Niezależnie od tego, czy określona metoda LINQ korzysta z trybu przesyłania strumieniowego, czy bez przesyłania strumieniowego, można znaleźć w witrynie MSDN - C # lub VB.NET .
Korzyści z odroczonego wykonania - budowanie zapytań
Odroczone wykonywanie umożliwia łączenie różnych operacji w celu zbudowania ostatecznego zapytania przed oszacowaniem wartości:
var list = new List<int>() {1,1,2,3,5,8};
var query = list.Select(x => x + 1);
Jeśli w tym momencie wykonamy zapytanie:
foreach (var x in query) {
Console.Write($"{x} ");
}
otrzymalibyśmy następujące dane wyjściowe:
2 2 3 4 6 9
Ale możemy zmodyfikować zapytanie, dodając więcej operatorów:
Console.WriteLine();
query = query.Where(x => x % 2 == 0);
query = query.Select(x => x * 10);
foreach (var x in query) {
Console.Write($"{x} ");
}
Wynik:
20 20 40 60
Korzyści z odroczonego wykonania - sprawdzanie bieżących danych
W przypadku odroczonego wykonania, jeśli zmieniane są dane, których dotyczy zapytanie, obiekt zapytania korzysta z danych w momencie wykonania, a nie w momencie definicji.
var data = new List<int>() {2, 4, 6, 8};
var query = data.Select(x => x * x);
Jeśli w tym momencie wykonamy zapytanie metodą natychmiastową lub foreach
, zapytanie będzie działać na liście liczb parzystych.
Jeśli jednak zmienimy wartości na liście:
data.Clear();
data.AddRange(new [] {1, 3, 5, 7, 9});
lub nawet jeśli przypiszemy nową listę do data
:
data = new List<int>() {1, 3, 5, 7, 9};
a następnie wykonać zapytanie, zapytanie będzie działać na nowej wartości data
:
foreach (var x in query) {
Console.Write($"{x} ");
}
i wyświetli następujące informacje:
1 9 25 49 81