C #: возможно ли объявить локальную переменную в анонимном методе? - PullRequest
13 голосов
/ 15 декабря 2008

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

Есть ли "разрешить" для анонимных функций?

Обновление: Обратите внимание, что после этого оператора я добавляю несколько предложений Where, поэтому не могу завершить выбор.

/ Niels

Ответы [ 6 ]

26 голосов
/ 15 декабря 2008

Да, почему нет ?! Ведь это функция, просто анонимная!

Пример:

 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 для запуска на сервере. Он не выполняется как метод и не считается анонимным методом. В этом случае вы не можете определить локальную переменную, поскольку невозможно проанализировать лямбду как дерево выражений.

7 голосов
/ 15 декабря 2008

Да, вы можете делать именно то, что вы хотите, в Linq для объектов и Linq в 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);
2 голосов
/ 15 декабря 2008

Если вы используете Linq to SQL, вы не сможете использовать ответ Мехрдада Афшари. Ваши выражения LINQ должны быть деревьями выражений, и они не поддерживают синтаксис анонимного делегата.

Также вы не сможете создать свой делегат в другом месте и вызывать его изнутри лямбда - Linq to SQL позволяет выполнять только определенные операции в теле запроса, и вызов делегата не является одним из них.

Лучше всего, если вы используете Linq to SQL (как это видно из вашего примера), - уменьшить счетчик в одном запросе, а затем зафиксировать переменную count в запросе, который требует счет.

1 голос
/ 15 декабря 2008

Метод Where берет Func, поэтому то, что вы передаете во второй части, на самом деле не метод, а просто выражение bool. Мое предложение было бы иметь фактический метод, который возвращает bool, который принимает необходимые вам параметры, и при вызове метода Where вы просто делаете что-то вроде этого где (p => MyMethod (p, ...))

0 голосов
/ 24 декабря 2008

Я столкнулся с подобной проблемой. Решением является создание собственного метода генерации дерева выражений.

Я задал вопрос на MSDN-форумах. Пожалуйста, смотрите вопрос и ответ здесь: Повторное использование выражений Where .

Это может дать вам представление о том, как поступить, но я должен признать, что деревья пользовательских выражений не для слабонервных; -)

0 голосов
/ 16 декабря 2008

Имея небольшой опыт работы с Scheme, вы знаете, что 'let' - это просто синтаксический сахар для определения лямбды и ее вызова.

Итак, с этим знанием, давайте посмотрим, как это можно сделать.

(count => x <= count && count <= y)
  ((from p in db.Orders 
    where p.EnquiryId == e.Id 
    select p).Count())

В качестве бонуса она тоже выглядит как Схема:)

Отказ от ответственности: я не тестировал этот фрагмент, но нет причин, по которым он не должен работать. Лично я бы просто использовал конструкцию let, предоставленную в LINQ.

Обновление:

Не работает ...: (

...