2010-10-19 13 views
5

Digamos que usted tiene el siguiente código:Cómo insertar una lambda en una expresión LINQ consulta declarativa

string encoded="9,8,5,4,9"; 

    // Parse the encoded string into a collection of numbers 
    var nums=from string s in encoded.Split(',') 
      select int.Parse(s); 

Eso es fácil, pero lo que si quiero aplicar una expresión lambda a s en la selección, pero todavía mantener esto como una expresión de consulta declarativa, en otras palabras:

string encoded="9,8,5,4,9"; 

    // Parse the encoded string into a collection of numbers 
    var nums=from string s in encoded.Split(',') 
      select (s => {/* do something more complex with s and return an int */}); 

Esto por supuesto no se compila. Pero, ¿cómo puedo obtener una lambda allí sin cambiar esto a sintaxis fluida.

actualización: Gracias a la orientación de StriplingWarrior, tengo una solución complicada, pero compilables:

var result=from string s in test.Split(',') 
      select ((Func<int>) 
      (() => {string u="1"+s+"2"; return int.Parse(u);}))(); 

La clave está en el yeso a un Func<string,int> seguido de la evaluación de la lambda para cada iteración del selecto con (s). ¿Alguien puede encontrar algo más simple (es decir, sin el elenco de Func seguido de su evaluación o tal vez algo menos detallado que logra el mismo resultado final manteniendo la sintaxis de la expresión de consulta)?

Nota: El contenido de lambda anterior es de naturaleza trivial y ejemplar. Por favor no lo cambies.

Actualización 2: Sí, soy yo, loco Mike, de vuelta con una alternativa de solución (más bonita?) A esto:

public static class Lambda 
{ 
    public static U Wrap<U>(Func<U> f) 
    { 
    return f(); 
    } 
} 
... 
    // Then in some function, in some class, in a galaxy far far away: 

    // Look what we can do with no casts 
    var res=from string s in test.Split(',') 
      select Lambda.Wrap(() => {string u="1"+s+"2"; return int.Parse(u);}); 

Creo que esto resuelve el problema sin el reparto y parenarrhea feo. ¿Hay algo así como el método genérico Lambda.Wrap ya presente en algún lugar del Framework .NET 4.0, para que no tenga que reinventar la rueda? Para no sobrecargar esta discusión, he trasladado este punto a su propia pregunta: Does this "Wrap" generic method exist in .NET 4.0.

+1

No entiendo por qué alguien pueda desear utilizar la sintaxis declarativa. Su semejanza con sql parece confundir a los recién llegados con linq más de lo que les ayuda a entender qué es linq. Pero una buena pregunta de todos modos. +1 –

+1

@klausbyskov, ha habido muchas preguntas sobre el "por qué" en SO que han sido respondidas. Me preocupa menos abordar el porqué de esta pregunta y solo quiero una respuesta al "cómo", si es posible. De acuerdo, mi ejemplo no es el mejor candidato para una sintaxis fluida, pero quería utilizar un ejemplo simple para no complicar demasiado mi pregunta. –

+1

La sintaxis declarativa es útil cuando tienes varios 'join's o' from's, que de otra manera necesitarían algunos tipos anónimos codificados a mano muy sucios. – StriplingWarrior

Respuesta

2

Suponiendo que está usando LINQ a objetos, es posible que utilices un método de ayuda:

select DoSomethingComplex(s) 

Si no te gusta métodos, se puede utilizar un Func:

Func<string, string> f = s => { Console.WriteLine(s); return s; }; 
var q = from string s in new[]{"1","2"} 
     select f(s); 

O Si está completamente decidido a ponerlo en línea, podría hacer algo como esto:

from string s in new[]{"1","2"} 
select ((Func<string>)(() => { Console.WriteLine(s); return s; }))() 
+0

Al usar llaves {}, de hecho está diciendo que su expresión lambda representa una función. – StriplingWarrior

+0

Creo que estás en la respuesta. La clave está en el molde para Func (no cadena) de la expresión lambda. Déjame jugar un poco más, pero tu respuesta está en el camino correcto, así que +1 por ahora ... –

+1

Sí, la T en 'Func ' debe ser del tipo que esperas * obtener * de la expresión . Su ejemplo usa 'cadena' porque simplemente está devolviendo' s' al final del día. –

0

Simplemente podría hacer:

var nums = from string s in encoded.Split(',') 
      select (s => { DoSomething(); return aValueBasedOnS; }); 

El retorno le dice al compilador el tipo de la colección resultante.

+0

Intenté algo similar a esto, pero no se compilará: var nums = desde cadena s en test.Split ('_') select {t => {int n = DoSomething (t); ... return n;}}; // Donde DoSomething se define como: int DoSomething (cadena s) ... –

+0

Corrección a la expresión anterior (aún no se compilará): var resultado = de cadena s en test.Split ('_') select (t => {return int.Parse (DoSomething (t));}); –

0

¿Qué tal esto:

var nums= (from string s in encoded.Split(',') select s).Select(W => ...); 
0

¿Alguien puede llegar a nada más simple?

Sí.En primer lugar, usted podría reescribirla como esto

var result = from s in encoded.Split(',') 
      select ((Func<int>)(() => int.Parse("1" + s + "2")))(); 

Sin embargo, eso no es muy fácil de leer, sobre todo para una expresión de consulta. Para esta consulta y proyección en particular, podría usarse la palabra clave let.

var result = from s in encoded.Split(',') 
      let t = "1" + s + "2" 
      select int.Parse(t); 
+0

No me molesta el espacio intermedio, pero ¿cómo puedo obtener una expresión lambda allí, no "1" + s + "2". Eso fue solo un ejemplo ... –

+0

De la misma forma que lo haría con el 'Seleccionar'. Tales como 'let t = ((Func ) (() =>" 1 "+ s +" 2 "))()'. Una vez más, está de vuelta en el ámbito de marginalmente ilegible, y más aún si tiene la intención de hacer más. Una vez más, digo que sus preguntas/comentarios me ponen nervioso en cuanto a cuál es su intención * real *. –

+0

@Michael, la conclusión es que al usar la sintaxis de la expresión de la consulta, simplemente necesita decirle al compilador acerca de su lambda mediante conversión a cualquier 'Func <>' aplicable para la expresión. Además, deberá invocar la expresión con todos los parámetros que sean apropiados.Pero simplemente quiero decir para el registro: se muy cauteloso. –

0

enteros IEnumerable = encoded.Split ('') SELECT (s => int.Parse (s)).;

Editar:

IEnumerable<int> integers = from s in encoded.Split(',') select int.Parse(string.Format("1{0}2",s)); 
+0

Creo que te perdiste el objetivo de la pregunta, que era evitar llamar '.Seleccione (...)' – StriplingWarrior

Cuestiones relacionadas