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

StackOverflow https://stackoverflow.com/questions/369334

Вопрос

Возможно ли иметь локальную переменную в анонимных методах 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.

Обновить:

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top