2010-01-07 5 views
8

Tengo una consulta LINQ que tiene este aspecto:Sustitución de un método regular con un método anónimo en C#/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; 
} 

me gustaría deshacerse de AddMissingProp y utilizar alguna forma de lambda en mi select cláusula en su lugar.

intenté ...

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

... pero tengo el siguiente 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.

¿Cómo puedo "lambda-ize" mi consulta?


actualización

Esto también no funciona:

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

me sale el siguiente error:

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

que no cambió la cláusula join en todo, así que estoy perplejo.

+1

No conozco la sintaxis LINQ lo suficiente como para escribirla, pero ¿no sería mejor hacer la mutación en una declaración foreach (yourLinqQuery)? Tener una mute selectiva de los objetos parece confuso. –

+0

try * on f.BarId es igual a b.Id * –

+0

@Doc Brown, en realidad fue solo un error tipográfico en mi ejemplo (mi código real dice 'igual'). Corregido ahora. – devuxer

Respuesta

3

Puede dar tipos a sus parámetros en una expresión lambda pero necesita usar nombres diferentes ya que está usando f y b en la consulta.

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

Editar

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

esto tampoco funcionaría. –

+0

Esto no funciona. No sabe que debería pasar en 'f' y' b'. También recibo el mismo error que si uso '() => {...}' (ver mi actualización). – devuxer

+0

Ver mi edición por favor ... –

1

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

+0

esto no funcionaría. –

+0

Esto no funciona. No sabe que debería pasar en 'f' y' b'. También recibo el mismo error que si uso() => {...} (ver mi actualización). – devuxer

1

Vuelva a escribir esto con la sintaxis Lambda.

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

Si necesita una explicación de esta sintaxis, hágamelo saber.

+0

Por alguna razón, esto causa un error con la cláusula join (aunque no cambié la cláusula join): * El tipo de una de las expresiones en la cláusula join es incorrecto. La inferencia de tipo falló en la llamada a 'Unirse'. * – devuxer

+0

es porque cuando selecciona que está diciéndole qué tipo devolver, select() asume que está devolviendo un tipo de delegado. Creo que deberías seguir con tu método. –

+0

Hmm ... tal vez simplemente no se puede hacer. Pero ¿por qué no puede inferir el tipo de retorno de 'return f;'? Sabe que f es un 'Foo'. – devuxer

5

creo icambron es correcto, en mi humilde opinión la mejor versión legible es la siguiente:

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

Las declaraciones se from join select para las consultas, no deben ser mal utilizados para mutar el contenido de una secuencia.

EDITAR: Aquí hay otra link que proporciona algunas ideas sobre por qué no es una buena idea usar una forma funcional de ForEach.

+0

Gracias +1. Había pensado en utilizar 'foreach', pero creo que estaba tratando de ser demasiado elegante :) Esta es una buena solución. – devuxer

+0

Vaya, un problema. Esto me deja con un 'IEnumerable ' pero necesito un 'IEnumerable '. Creo que si agrega 'return fooBar.Select (fb => fb.f);' a su respuesta, será correcto. – devuxer

+2

También debería poder hacer 'var foobar = (....). ForEach (....); 'y una línea! –

1

Si el compilador no puede inferir el tipo correcto para pasar a una lambda, puede especificar el tipo usted mismo.

Esto debería funcionar bien:

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

Tenga en cuenta que como ya ha notado, no se puede volver a utilizar f y la esperanza de que retendrá su significado.Esta es una nueva firma de método, con nuevos parámetros, por lo que debe usar un nombre distinto para ella.

Cuando lo hace, observa que el compilador no puede determinar por sí mismo de qué tipo debe ser el primer argumento, pero puede especificarlo, como se indicó anteriormente.

+0

En cuanto al código incompleto, olvidé mi declaración de devolución. Solo lo arregle. – devuxer

+0

También se ha perdido el punto y coma final después de la consulta de Linq. Siempre debe intentar copiar y pegar lo que tiene en su código, no tratar de simplificarlo. Te sorprendería lo que las personas aquí pueden leer sobre el código :) –

+0

eliminó la última parte de la respuesta para reflejar la pregunta editada. –

Cuestiones relacionadas