С#: Можно ли объявить локальную переменную анонимным методом?
Возможно ли иметь локальную переменную в анонимных методах С#, то есть в следующем коде, я хотел бы выполнить счет только один раз.
IQueryable<Enquiry> linq = db.Enquiries;
if(...) linq = linq.Where(...);
if(...) linq = linq.Where(e =>
(x <= (from p in db.Orders where p.EnquiryId == e.Id select p).Count() &&
(from p in db.Orders where p.EnquiryId == e.Id select p).Count() <= y));
if(...) linq = linq.Where(...);
var result = (from e in linq select e);
Есть ли "let" для анонимных функций?
Обновление:
Обратите внимание, что я добавляю несколько предложений Where после этого утверждения, поэтому я не могу закрыть с помощью select.
/Niels
Ответы
Ответ 1
У меня возникла аналогичная проблема. Решение состоит в том, чтобы создать собственный метод генерации дерева выражений.
Я задал свой вопрос на форумах MSDN. См. Вопрос и ответ здесь: Повторное использование Где выражения.
Это может дать вам представление о том, как действовать, но я должен признать, что пользовательские деревья выражений не для слабонервных; -)
Ответ 2
Да, почему бы и нет?! Ведь это функция, просто анонимная!
Пример:
x => { int y = x + 1; return x + y; }
Или, альтернативно:
delegate(int x) {
int y = x + 1;
return x + y;
}
Итак, ваш код можно записать как:
... = linq.Where(e => {
var count = (from p in db.Orders where p.EnquiryId == e.Id select p).Count();
return x <= count && count <= y;
});
ОБНОВЛЕНИЕ: Чтобы прояснить ситуацию в комментарии, важно знать разницу между анонимными методами и лямбда-выражениями. Анонимный метод похож на обычный метод без явного имени. Когда вы скомпилируете его, компилятор генерирует обычный метод с странным именем для вас, поэтому он не будет иметь никаких особых ограничений. Однако одно представление анонимного метода является выражением лямбда. Лямбда-выражения можно интерпретировать несколькими способами. Первый - делегат. Таким образом, они равны анонимному методу. Второй - это дерево выражений. Этот путь обычно используется LINQ to SQL и некоторыми другими поставщиками LINQ. Они не выполняют ваше выражение напрямую никакими средствами. Они анализируют его как дерево выражений и используют дерево в качестве входных данных для генерации эквивалентного оператора SQL, который должен быть запущен на сервере. Он не выполняется как метод и не считается анонимным методом. В этом случае вы не можете определить локальную переменную, так как невозможно проанализировать лямбда как дерево выражений.
Ответ 3
Да, вы можете делать именно то, что хотите, в Linq для объектов и Linq to SQL.
В Linq есть let
, позволяющий вам дать имя промежуточному результату в середине вашего запроса, как вы хотите. На основе вашего примера:
... = from e in linq
let count = (from p in db.Orders where p.EnquiryId == e.Id select p).Count()
where (x <= count) && (count <= y)
select e;
Кстати, я думаю, что в вашем исходном примере было что-то синтаксически ошибочное, что легче обнаружить, когда count
- это просто имя:
where (x <= count) && /* <= */ (count <= y);
Ответ 4
Если вы используете Linq для SQL, вы не сможете использовать ответ Mehrdad Afshari. Ваши выражения LINQ должны быть деревьями выражений, и они не поддерживают синтаксис анонимного делегата.
Также вы не сможете создать делегат в другом месте и вызвать его изнутри лямбда - Linq to SQL только позволяет выполнять определенные операции в теле запроса, а вызов делегата не является одним из них.
Лучше всего предположить, что вы используете Linq to SQL (как показано в вашем примере), заключается в том, чтобы свести количество в одном запросе, а затем захватить переменную count в запросе, для которого требуется счет.
Ответ 5
Метод Where принимает Func так, что вы проходите там во второй части, на самом деле это не метод, а просто выражение bool. Мое предложение состояло в том, чтобы иметь реальный метод, который возвращает bool, который принимает нужные вам параметры, и в вашем вызове метода Where вы просто делаете что-то вроде этого Where (p = > MyMethod (p,...))
Ответ 6
С небольшим фоном в Схеме вы бы знали, что "let" - это просто синтаксический сахар для определения лямбды и вызова его.
Итак, с помощью этого знания давайте посмотрим, как это можно сделать.
(count => x <= count && count <= y)
((from p in db.Orders
where p.EnquiryId == e.Id
select p).Count())
В качестве бонуса он выглядит также как схема:)
Отказ от ответственности: я не тестировал этот фрагмент, но нет причин, по которым он не должен работать. Лично я бы просто использовал конструкцию "let", предоставленную в LINQ.
Update:
Это не работает...: (