Правильний Linq, де застереження


133

Я пишу неабияку кількість посилань у своєму щоденному житті, але переважно прості заяви. Я помітив, що при використанні пропозицій де є багато способів їх написання, і кожен з них має однакові результати, наскільки я можу сказати. Наприклад;

from x in Collection
  where x.Age == 10
  where x.Name == "Fido"
  where x.Fat == true
  select x;

Здається, це рівнозначно принаймні, що стосується результатів:

from x in Collection
  where x.Age == 10 &&
        x.Name == "Fido" &&
        x.Fat == true
  select x;

То чи є насправді різниця, крім синтаксису? Якщо так, то який переважний стиль і чому?


203
У вас булева Fatвластивість? Це звичайне значення.
Бала Р

104
@Bala R: Гей, якщо ваша собака товста, ваша собака товста.
AR

Відповіді:


76

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

Також ланцюжок (перший метод) буде працювати лише в тому випадку, якщо ви ANDing ваші предикати. Щось подібне x.Age == 10 || x.Fat == trueне буде працювати з вашим першим методом.


1
Умови ланцюга ORing дещо можливі за допомогою цього розширення: albahari.com/nutshell/predicatebuilder.aspx
jahu

142

EDIT: LINQ до об'єктів не поводиться так, як я очікував. Можливо, вас зацікавить повідомлення в блозі, про який я щойно писав ...


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

Collection.Where(x => x.Age == 10)
          .Where(x => x.Name == "Fido")
          .Where(x => x.Fat == true)

коли останній еквівалентний:

Collection.Where(x => x.Age == 10 && 
                      x.Name == "Fido" &&
                      x.Fat == true)

Тепер, яка різниця, що насправді має значення, залежить від того, наскільки Whereвони викликаються. Якщо це постачальник, що базується на SQL, я очікую, що вони в кінцевому підсумку створять один і той же SQL. Якщо він знаходиться в LINQ to Objects, другий матиме менший рівень непрямості (замість чотирьох будуть задіяні два ітератори). Чи важливі ці рівні опосередкованості щодо швидкості - це вже інше питання.

Як правило, я б використовував декілька whereзастережень, якщо їм здається, що вони представляють значно різні умови (наприклад, це стосується однієї частини об'єкта, а інша - повністю окрема частина) та один whereпункт, коли різні умови тісно пов'язані (наприклад, певне значення більше мінімуму і менше максимуму). В основному варто враховувати читабельність перед будь-якою незначною різницею у виконанні.


1
@JonSkeet Можливо, я помиляюся, але після швидкого огляду Linq Де реалізація, я не впевнений у цьому. Вкладені Де поєднуються статичним методом "CombinePredicate". Збірник повторюється лише один раз одним ітератором із поєднаним присудком. Звичайно, є комбінування функцій на продуктивність, але це дуже обмежено. Все гаразд ?
Кібермакси

@Cybermaxs: Не знаєте, що саме? Я ніколи не припускав, що колекцію повторять не раз.
Джон Скіт

@JonSkeet так, але, в кінці кінців, всі предикати поєднуються, і лише один ітератор викликається. Подивіться наEnumerable.WhereSelectEnumerableIterator.
Кібермакси

Сторінка, на яку ви пов’язали, зараз не працює. Чи можете ви оновити посилання, якщо стаття все ще є десь? Дякую.
Асад Саедюддін

2
@Asad: оновлено. (Мій блог перемістився.)
Джон Скіт

13

Перший буде реалізований:

Collection.Where(x => x.Age == 10)
          .Where(x => x.Name == "Fido") // applied to the result of the previous
          .Where(x => x.Fat == true)    // applied to the result of the previous

На відміну від набагато простішого (і набагато швидшого, мабуть, швидшого):

// all in one fell swoop
Collection.Where(x => x.Age == 10 && x.Name == "Fido" && x.Fat == true)

6
"Куди швидше"? Ми навіть не знаємо, в якій реалізації LINQ йдеться, тому важко приєднати до неї будь-які наслідки.
Джон Скіт

У загальному випадку для останнього потрібна лише 1 петля. Постачальник може вирішити вирівняти перший приклад, але це не потрібно.
user7116

2
На насправді ... але ви стверджуючи , що останній буде набагато швидше. Зовсім не зрозуміло, що це взагалі буде значно швидше - адже залежність від ефективності залежатиме від того, як це використовується.
Джон Скіт

1
@Jon: розбіжностей немає. Як ви зауважуєте, реальність може бути провайдером LINQ і виконувати корисні оптимізаційні перетворення до виразу. Але з огляду на те, що другий вимагає лише одного циклу і отримує перевагу від булевого короткого замикання, важко зрозуміти, чому його не слід мітити як «набагато швидше» в загальних рисах. Якщо в ОП є лише 5 елементів, то моя суть є суперечкою.
user7116

11

коли я біжу

from c in Customers
where c.CustomerID == 1
where c.CustomerID == 2
where c.CustomerID == 3
select c

і

from c in Customers
where c.CustomerID == 1 &&
c.CustomerID == 2 &&
c.CustomerID == 3
select c customer table in linqpad

проти моєї таблиці клієнтів він видає той же запит sql

-- Region Parameters
DECLARE @p0 Int = 1
DECLARE @p1 Int = 2
DECLARE @p2 Int = 3
-- EndRegion
SELECT [t0].[CustomerID], [t0].[CustomerName]
FROM [Customers] AS [t0]
WHERE ([t0].[CustomerID] = @p0) AND ([t0].[CustomerID] = @p1) AND ([t0].[CustomerID] = @p2)

тому в перекладі на sql немає різниці, і ви вже бачили в інших відповідях, як вони будуть перетворені на лямбда-вирази


Гаразд, тоді ви хочете сказати, що це не матиме жодного ефекту від продуктивності, якщо я використовую будь-яке з них?
Бімал Дас

ЧОМУ фактично прикуті пропозиції. Отже, не має значення, як ви це пишете. Різниці в продуктивності немає.
hastrb

3

Заглянувши під капот, два твердження перетворяться на різні представлення запитів. Залежно QueryProviderвідCollection , це може бути оптимізовано далеко чи ні.

Коли це виклик зв'язку-об'єкта, множинне, де пункти призводять до ланцюжка IEnumerables, які читаються один з одного. Використання форми з одним застереженням допоможе тут зробити ефективність.

Коли базовий провайдер переводить його в оператор SQL, великі шанси, що обидва варіанти створять один і той же оператор.

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