C#:Можно ли объявить локальную переменную в анонимном методе?
-
21-08-2019 - |
Вопрос
Возможно ли иметь локальную переменную в анонимных методах 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.
/Нильс
Решение
Я столкнулся с аналогичной проблемой.Решение состоит в том, чтобы создать пользовательский метод генерации дерева выражений.
Я задал свой вопрос на 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;
});
Обновить:Чтобы прояснить ситуацию с комментарием, важно знать разницу между анонимными методами и лямбда-выражениями.Анонимный метод ничем не отличается от обычного метода, только без явного имени.Когда вы его компилируете, компилятор генерирует для вас обычный метод со странным именем, поэтому у него не будет никаких особых ограничений.Однако одним из представлений анонимного метода является лямбда-выражение.Лямбда-выражения могут быть интерпретированы несколькими различными способами.Первый - это делегат.Таким образом, они равны анонимному методу.Второй - это дерево выражений.Этот способ обычно используется LINQ to SQL и некоторыми другими поставщиками LINQ.Они ни в коем случае не выполняют ваше выражение напрямую.Они анализируют его как дерево выражений и используют дерево в качестве входных данных для генерации эквивалентной инструкции SQL, которая будет запущена на сервере.Он не выполняется как метод и не считается анонимным методом.В этом случае вы не можете определить локальную переменную, поскольку невозможно проанализировать лямбда-выражение как дерево выражений.
Да, вы можете делать именно то, что хотите, в Linq to objects и Linq to SQL.
Существует let
в Linq это позволяет вам присвоить имя промежуточному результату в середине вашего запроса, как вы и хотите.Основываясь на вашем примере:
... = 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, вы не сможете использовать ответ Мехрдада Афшари.Ваши выражения LINQ должны быть Деревьями выражений, а они не поддерживают синтаксис анонимного делегата.
Вы также не сможете создать свой делегат в другом месте и вызвать его изнутри lambda - Linq to 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.
Обновить:
Это не работает...:(