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);

Есть ли «пусть» для анонимных функций?

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

/ Niels

15.12.2008 18:50:10
Вы пробовали это: var q = где l в linq let ct = (из p в db. Заказы, где p.EnquiryId == e.Id select p) .Count () select l;
Erik Forbes 15.12.2008 19:26:15
@Eric - вам нужно начинать с «от» вместо «где». Затем упоминается переменная e, но она нигде не определена. Также определено, но не используется. Наконец, даже если значение было изменено на from, результат будет содержать то же самое, что и linq (входные данные). Кроме того, это здорово.
Daniel Earwicker 15.12.2008 20:29:24
6 ОТВЕТОВ
РЕШЕНИЕ

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

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

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

0
23.12.2008 23:26:00

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

Пример:

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

26
18.12.2008 07:47:09
Это не сработает. Вы получите ошибку «Лямбда-выражение с телом оператора не может быть преобразовано в дерево выражений».
BFree 15.12.2008 18:59:08
Проблема в том, что спрашивающий не правильно сформулировал вопрос - он действительно хочет знать об этом в контексте запроса Linq to SQL, а не запроса Linq to Objects. Отстой, что мы должны относиться к ним по-разному, но у вас это есть.
Erik Forbes 15.12.2008 19:23:22
«В этом случае [Linq to SQL] вы не можете определить локальную переменную, поскольку невозможно проанализировать ее как дерево выражений». - кроме как вы можете, если вы прочитаете мой ответ на этот вопрос.
Daniel Earwicker 15.12.2008 20:25:03
Ваше утверждение об отсутствии ограничений неверно. Существует множество ограничений на лямбда-выражения / анонимные функции. Например, попробуйте использовать base.Something в анонимной функции C #. В зависимости от вашей версии вы получите предупреждение или сбой при компиляции
JaredPar 15.12.2008 20:29:59
@Earwicker: В вашем примере вы не определяете локальную переменную в теле лямбда-выражения. Используя let, вы не определили локальную переменную в своей лямбде. Я говорю о лямбда-выражениях, а не о возможностях LINQ to SQL.
Mehrdad Afshari 16.12.2008 04:48:32

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

1
15.12.2008 18:57:31

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

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

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

2
15.12.2008 19:01:40

Да, вы можете делать именно то, что вы хотите, в Linq для объектов и Linq для SQL.

В letLinq есть опция, позволяющая вам дать имя промежуточному результату в середине вашего запроса, как вы и хотите. На основании вашего примера:

... = 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);
7
16.12.2008 08:12:32
Это не работает, я получаю синтаксическую ошибку о том, что запрос должен заканчиваться выбором. Обратите внимание, что мне нужно добавить несколько предложений Where, чтобы я не мог завершить запрос.
Niels Bosma 16.12.2008 07:32:03
Я добавил «выбрать e» выше, чтобы сделать пример правильным. Но что вы имеете в виду, что вы не можете закончить запрос? Вы должны закончить это чем-то, что заявляет, что это производит. Вы можете поставить несколько условий в предложении Where - просто добавьте больше к двум уже там.
Daniel Earwicker 16.12.2008 08:15:20

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

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

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

В качестве бонуса это тоже похоже на Схему :)

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

Обновить:

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

0
16.12.2008 21:00:35
Я предполагаю, что вы имеете в виду linq = linq.Where (e => ((count => (soldFrom <= count && count <= soldTo) ((из p в db.Orders, где p.EnquiryId == e.Id выберите p). Count ())); Это все еще не скомпилируется ...
Niels Bosma 16.12.2008 08:44:27
@leppie, проблема в том, что речь идет о LINQ to SQL. К сожалению, это указывается только тегом, а не в заголовке или описании. Ключевое слово let в LINQ - это не то же самое, что лямбда, вызываемая напрямую. Это Select, который производит последовательность пар ввода / вывода.
Daniel Earwicker 16.12.2008 15:49:25