Замена обычного метода анонимным методом в C # / LINQ - PullRequest
8 голосов
/ 07 января 2010

У меня есть запрос LINQ, который выглядит следующим образом:

public IEnumerable<Foo> SelectFooBars()
{
    return
        from
            f in foos
        join
            b in bars
            on f.BarId equals b.Id
        select
            AddMissingProp(f, b.MissingProp);
}

public void AddMissingProp(Foo foo, string missingProp) // substitute this with inline lambda
{
    foo.MissingProp = missingProp;
    return foo;
}

Я бы хотел избавиться от AddMissingProp и использовать вместо этого некоторую форму лямбды в моем предложении select.

Я пытался ...

...
select
    (f, b) => { f.MissingProp = b.MissingProp; return f }

... но я получил следующую ошибку:

Локальная переменная с именем 'f' не может быть объявлена ​​в этой области, поскольку она придала бы другое значение значению 'f', которое уже используется в области 'родительской или текущей' для обозначения чего-то другого.

Как я могу "лямбда-ize" мой запрос?


Обновление

Это также не работает:

...
select
    () => { f.MissingProp = b.MissingProp; return f }

Я получаю следующую ошибку:

Неверно указан тип одного из выражений в предложении соединения. Ошибка вывода типа при вызове «Join».

Я не изменил предложение join, поэтому я озадачен.

Ответы [ 5 ]

5 голосов
/ 07 января 2010

Я думаю Икамброн прав, ИМХО, лучше читаемая версия выглядит так:

  var fooBar = from 
                 f in foos
               join 
                 b in bars
                 on f.BarId equals b.Id 
               select new {f,b};

   foreach(var x in fooBar)
        x.f.MissingProp = x.b.MissingProp;

   // EDIT due to comments: add this if you 
   // need IEnumerable<Foo> returned
   return fooBar.Select(fb => fb.f);

Операторы from join select предназначены для запросов, их нельзя использовать неправильно для изменения содержимого последовательности.

РЕДАКТИРОВАТЬ: Вот еще одна ссылка , предоставляющая некоторые идеи, почему использование функциональной формы ForEach не очень хорошая идея.

3 голосов
/ 07 января 2010

Вы можете задать типы своим параметрам в лямбда-выражении, но вам нужно использовать разные имена, так как вы уже используете f и b в запросе.

(Foo f1, Bar b1) => ...

Редактировать

return
(
    from 
        f in foos 
    join
        b in bars 
        on f.BarId equals b.Id 
    select 
        new {f, b}
).select(foobar => {foobar.f.BarId = foobar.b.Id; return foobar.f});
1 голос
/ 08 января 2010

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

Это должно работать нормально:

select
    (Foo f2, b) => { f2.MissingProp = b.MissingProp; return f2; }

Обратите внимание, что, как вы уже заметили, вы не можете повторно использовать f и надеетесь, что оно сохранит свое значение. Это новая сигнатура метода с новыми параметрами, поэтому вам нужно использовать для нее отдельное имя.

Когда вы это делаете, вы замечаете, что компилятор не может самостоятельно определить тип первого аргумента, но вы можете указать его, как указано выше.

1 голос
/ 07 января 2010

Перепишите это с помощью синтаксиса Lambda.

var vf2 = foos.Join(bars, f => f.id, b => b.id, (foo, bar) => { foo.MissingProp = bar.MissingProp; return foo; });

Если вам нужно объяснение этого синтаксиса, дайте мне знать.

1 голос
/ 07 января 2010

выберите (f2, b2) => {f2.MissingProp = b2.MissingProp; return f2}

...