C#: можно ли объявить локальную переменную в анонимном методе?
Можно иметь локальную переменную в анонимных методах c#, т. е. в следующем коде я хотел бы выполнить подсчет только один раз.
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.
/ Нильс
6 ответов:
Я столкнулся с подобной проблемой. Решение заключается в создании пользовательского метода генерации дерева выражений.
Я задал свой вопрос на MSDN-форумах. Пожалуйста, смотрите вопрос и ответ здесь: повторное использование выражений Where .
Это может дать вам представление о том, как действовать, но я должен признать, что пользовательские деревья выражений не для слабонервных; -)
Да, почему бы и нет?! В конце концов, это функция, просто анонимная!
Пример:
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; });
UPDATE: чтобы прояснить ситуацию с комментарием, важно знать разницу между анонимными методами и лямбда-выражениями. Анонимный метод точно такой же, как и обычный метод, без явного имени. Когда вы его компилируете, компилятор генерирует обычный метод со странным именем для вас, так что у него не будет никаких особых ограничений. Однако одно из представлений анонимного метода - это лямбда-выражение. Лямбда-выражения можно интерпретировать несколькими различными способами. Первый-это делегат. Таким образом, они равны анонимному методу. Второе-это дерево выражений. Этот способ обычно используется LINQ to SQL и некоторыми другими поставщиками LINQ. Они ни в коем случае не выполняют ваше выражение напрямую. Они анализируют его как дерево выражений и используют дерево в качестве входных данных для создайте эквивалентную инструкцию SQL для запуска на сервере. Он не выполняется как метод и не считается анонимным методом. В этом случае вы не можете определить локальную переменную, поскольку невозможно проанализировать лямбду как дерево выражений.
Да, вы можете делать именно то, что хотите, в Linq to objects и 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);
Если вы используете Linq to SQL,вы не сможете использовать ответ Mehrdad Afshari. Выражения LINQ должны быть деревьями выражений, а они не поддерживают синтаксис анонимного делегата.
Кроме того, вы не сможете создать свой делегат в другом месте и вызвать его из лямбда - Линка в SQL, который позволяет выполнять только определенные операции в теле запроса, и вызов делегата не является одним из них.
Ваш лучший выбор, предполагая, что вы используете Linq to SQL (как это появляется в приведенном примере), состоит в том, чтобы сбить счетчик в одном запросе, а затем захватить переменную count в запросе, который требует подсчета.
Метод Where принимает функцию, поэтому то, что вы передаете во второй части, на самом деле не метод, а просто выражение bool. Мое предложение состояло бы в том, чтобы иметь реальный метод, который возвращает bool, который принимает в пареметры, которые вам нужны, и в вашем вызове метода Where вы просто делаете что-то вроде этого Where(p=> MyMethod(p,...))
С небольшим фоном в схеме вы бы знали, что " let " - это просто синтаксический сахар для определения лямбды и ее вызова.
Итак, с этим знанием давайте посмотрим, как это можно сделать.(count => x <= count && count <= y) ((from p in db.Orders where p.EnquiryId == e.Id select p).Count())
В качестве бонуса, это выглядит как схема тоже :)
Отказ от ответственности: я не тестировал этот фрагмент, но нет никаких причин, по которым он не должен работать. Лично я бы просто использовал конструкцию "let", предоставленную в LINQ.
Обновление:
Это не работает... : (