С#: Можно ли объявить локальную переменную анонимным методом?

Возможно ли иметь локальную переменную в анонимных методах С#, то есть в следующем коде, я хотел бы выполнить счет только один раз.

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:

Это не работает...: (