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 13

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.

Обновление:

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