devuxer
devuxer

Reputation: 42364

Replacing a regular method with an anonymous method in C#/LINQ

I have a LINQ query that looks like this:

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

I would like to get rid of AddMissingProp and use some form of a lambda in my select clause instead.

I attempted...

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

...but I got the following error:

A local variable named 'f' cannot be declared in this scope because it would give a different meaning to 'f', which is already used in a 'parent or current' scope to denote something else.

How can I "lambda-ize" my query?


Update

This also doesn't work:

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

I get the following error:

The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.

I didn't change the join clause at all, so I'm perplexed.

Upvotes: 8

Views: 4044

Answers (5)

Rodrick Chapman
Rodrick Chapman

Reputation: 5543

You can give types to your parameters in a lambda expression but you need to use different names since you're already using f and b in the query.

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

Edit

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

Upvotes: 3

Doc Brown
Doc Brown

Reputation: 20044

I think icambron is right, IMHO the better readable version is this:

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

The from join select statements are for queries, they should not be misused for mutating the contents of a sequence.

EDIT: Here is another link providing some insights why using a functional form of ForEach is not a good idea.

Upvotes: 5

Lasse V. Karlsen
Lasse V. Karlsen

Reputation: 391346

If the compiler isn't able to infer the correct type to pass to a lambda, you can of course specify the type yourself.

This should work fine:

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

Note that as you've already noticed, you cannot reuse fand hope that it will retain its meaning. This is a new method signature, with new parameters, so you need to use a distinct name for it.

When you do, you notice that the compiler isn't able to figure out by itself what type the first argument should be, but you can specify it, like above.

Upvotes: 1

Stan R.
Stan R.

Reputation: 16065

Rewrite this with Lambda syntax.

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

If you need explanation of this syntax, let me know.

Upvotes: 1

jjacka
jjacka

Reputation: 531

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

Upvotes: 1

Related Questions