Як працює наступний оператор LINQ?


160

Як працює наступний оператор LINQ ?

Ось мій код:

var list = new List<int>{1,2,4,5,6};
var even = list.Where(m => m%2 == 0);
list.Add(8);
foreach (var i in even)
{
    Console.WriteLine(i);
}

Вихід: 2, 4, 6, 8

Чому ні 2, 4, 6?


102
Результатом виразу запиту є запит, а не виконання запиту.
Ерік Ліпперт

6
Для отримання меншої інформації дивіться прийняту відповідь на це питання .
Даніель

9
Звичайно, ви можете придумати назву, яка насправді узагальнює питання.
Метт Бал

2
Моя здогадка про погіршення (6 на сьогоднішній день, а не моє) полягає в тому, що вони вважають заголовок питання занадто загальним, щоб бути хорошим питанням. Але, побачивши кількість оновлених даних і ставши головним питанням тижня в інформаційному бюлетені, я не думаю, що вам потрібно надто турбуватися про це.
Авель

Відповіді:


235

Вихід відбувається 2,4,6,8через відкладене виконання .

Запит фактично виконується, коли змінна запиту переглянута, а не коли створена змінна запиту. Це називається відкладеним виконанням.

- Suprotim Agarwal, "Відкладений проти негайного виконання запитів у LINQ"

Існує ще одне виконання, яке називається негайне виконання запиту , яке корисно для кешування результатів запиту. Знов Супротим Агарвал:

Щоб примусити негайно виконати запит, який не видає однотонного значення, ви можете викликати ToList(), ToDictionary(), ToArray(), Count(), Average()або Max() метод на змінну запиту чи запиту. Це називаються операторами перетворення, які дозволяють зробити копію / знімок результату та отримати доступ стільки разів, скільки не потрібно повторно виконувати запит.

Якщо ви хочете, щоб результат був 2,4,6, використовуйте .ToList():

var list = new List<int>{1,2,4,5,6};
var even = list.Where(m => m%2 == 0).ToList();
list.Add(8);
foreach (var i in even)
 {
    Console.WriteLine(i);
 }

8
Count (), Max (), Avg (), Sum () та, ймовірно, інші методи, які потребують врахування всього списку, також викликають оцінку запиту.
Кеннед

1
Я часто замислювався над тим, щоб, скажімо, "filteredList" як змінною, а не "filterList ()" як метод - ідея полягає в тому, що ви повторюєте його щоразу, коли список потрібний, а не викликати метод. Можливо, це буде цікавий, якщо незвичний і, можливо, недосконалий метод виконання дій.
Katana314

4
@Sebastian - В доповнення до @ коментар Kenned, в .First(), .FirstOrDefault(), .Single()а .SingleOrDefault()також викликати обчислення запиту.
Scotty.NET

4
дивно, як ви отримали відповідь менш ніж за 30 сек: D
MC

2
@MC Я не знаю, чому ви задаєте це питання. Повної відповіді не було надано одночасно. Його редагували кілька разів.
Атіш Діпонгкор - MVP

11

Це сталося через відкладене виконання, а це означає, що обчислення виразу не виконується, поки воно не потрібно десь. Це робить ефективність кращою, якщо дані занадто великі.


3
Ви можете відтінити це, оскільки це також може означати, що ваше дороге перерахування виконується кілька разів. У такому випадку ви навіть можете зазнати втрати продуктивності.
Гримаса відчаю

0

Причиною цього є відкладене виконання вашого лямбда-виразу. Запит виконується, коли ви починаєте ітерацію в циклі foreach.


11
Технічно це відкладене виконання ітератора , а не лямбда .
D Стенлі

0

Коли ви використовуєте IEnumerable <>, отриманий від LINQ, створюється лише клас Enumerator, і ітерація починається лише тоді, коли ви використовуєте його на деякій прогулянці.


-1

Ви отримуєте цей результат через відкладене виконання, тобто результат насправді не оцінюється до його першого доступу.

Щоб зробити це більш зрозумілим, просто додайте 10 до списку в кінці свого фрагмента, а потім знову надрукуйте, ви не отримаєте 10 у виводі

     var list = new List<int>{1,2,4,5,6};
    var even = list.Where(m => m%2 == 0).Tolist();
    list.Add(8);
    foreach (var i in even)
    {
        Console.WriteLine(i);
    }
//new*
    list.Add(10);
    foreach (var i in even)
    {
        Console.WriteLine(i);
    }

Ви насправді пробували це? Я отримую 10на виході.
Марк Херд

хороший улов @MarkHurd так не додано .ToList (). редагував пост зараз, він повинен дати очікуваний вихід. Моє сподівання, що вираження оцінюється лише тоді, коли ви вперше використовуєте var, але, схоже, щоразу його оцінюють
sandeep

Тепер він не міститиме 8жодного виводу.
Марк Херд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.