Pregunta

Tengo el siguiente código:

string prefix = "OLD:";
Func<string, string> prependAction = (x => prefix + x);
prefix = "NEW:";
Console.WriteLine(prependAction("brownie"));

Debido a que el compilador sustituye el prefijo de variable con un "cierre de NUEVO:brownie" se imprime en la consola.

Hay una manera fácil de evitar que el compilador de levantar el prefijo de variable, mientras que todavía haciendo uso de una expresión lambda?Me gustaría una manera de hacer que mi Func funcionan de forma idéntica a:

Func<string, string> prependAction = (x => "OLD:" + x);

La razón por la que necesita este, es que me gustaría para serializar el resultado delegado.Si el prefijo de variable en una no-clase serializable la función de arriba no va a serializar.

La única manera de evitar esto lo puedo ver en este momento es crear una nueva clase serializable que las tiendas de la cadena como un miembro de la variable y tiene la cadena de anteponer método:

string prefix = "NEW:";
var prepender = new Prepender {Prefix = prefix};
Func<string, string> prependAction = prepender.Prepend;
prefix = "OLD:";
Console.WriteLine(prependAction("brownie"));

Con clase auxiliar:

[Serializable]
public class Prepender
{
    public string Prefix { get; set; }
    public string Prepend(string str)
    {
        return Prefix + str;
    }
}

Esto parece como un montón de trabajo extra para obtener el compilador de ser "tonto".

¿Fue útil?

Solución

Veo que el problema subyacente ahora.Es más profundo de lo que yo pensaba.Básicamente, la solución es modificar el árbol de expresión antes de la serialización, por la sustitución de todos los subárboles que no dependen de los parámetros constante de los nodos.Al parecer, esto es llamado "funcletization".Hay una explicación de aquí.

Otros consejos

Acaba de hacer otro cierre...

Decir, algo como:

var prepend = "OLD:";

Func<string, Func<string, string>> makePrepender = x => y => (x + y);
Func<string, string> oldPrepend = makePrepender(prepend);

prepend = "NEW:";

Console.WriteLine(oldPrepend("Brownie"));

Aun no probado aún, ya que no tienen acceso a la VS en el momento, pero normalmente, esto es cómo voy a resolver dicho problema.

Las Lambdas automáticamente 'chupar' en las variables locales, me temo que simplemente cómo funcionan, por definición.

Este es un problema bastante común decirlas variables que están siendo modificados por un cierre involuntariamente, una mucho más simple solución es sólo para ir a:

string prefix = "OLD:";
var actionPrefix = prefix;
Func<string, string> prependAction = (x => actionPrefix + x);
prefix = "NEW:";
Console.WriteLine(prependAction("brownie"));

Si usted está usando resharper es realmente identificar los lugares en el código que usted está en riesgo de causar efectos secundarios inesperados como este - de modo que si el archivo es "todo verde" de su código debería estar bien.

Creo que de alguna manera habría sido agradable si hemos tenido algo de azúcar sintáctico para controlar esta situación por lo que podría haber escrito como un one-liner es decir,

Func<string, string> prependAction = (x => ~prefix + x);

Donde algunos operador de prefijo haría que el valor de la variable a ser evaluada antes de la construcción de la anónima delegado/función.

Tengo el problema ahora:la lambda se refiere a la clase que las contiene, que podría no ser serializable.A continuación, hacer algo como esto:

public void static Func<string, string> MakePrependAction(String prefix){
    return (x => prefix + x);
}

(Nota: la palabra clave static.) A continuación, la lambda no necesita hacer referencia a la clase que las contiene.

Ya hay varias respuestas que aquí se explica cómo se puede evitar el lambda de "levantar" la variable.Por desgracia, eso no resuelve el problema subyacente.No ser capaz de serializar la lambda no tiene nada que ver con la lambda de haber "levantado" de su variable.Si la expresión lambda necesidades de una instancia de un no-serializar la clase para calcular, tiene perfecto sentido de que no puede ser serializado.

Dependiendo de lo que realmente están tratando de hacer (yo no puedo decidir de su post), una solución sería mover el no serializable parte de la lambda en el exterior.

Por ejemplo, en lugar de:

NonSerializable nonSerializable = new NonSerializable();
Func<string, string> prependAction = (x => nonSerializable.ToString() + x);

uso:

NonSerializable nonSerializable = new NonSerializable();
string prefix = nonSerializable.ToString();
Func<string, string> prependAction = (x => prefix + x);

Lo que acerca de esto

string prefix = "OLD:";
string _prefix=prefix;
Func<string, string> prependAction = (x => _prefix + x);
prefix = "NEW:";
Console.WriteLine(prependAction("brownie"));

Cómo acerca de:

string prefix = "OLD:";
string prefixCopy = prefix;
Func<string, string> prependAction = (x => prefixCopy + x);
prefix = "NEW:";
Console.WriteLine(prependAction("brownie"));

?

Bueno, si vamos a hablar de "problemas", de lambdas vienen desde el mundo de la programación funcional, y exclusivamente en un lenguaje de programación funcional, no hay asignaciones y así, su problema sería nunca surgen debido a que el prefijo del valor nunca podría cambiar.Entiendo C# piensa que es genial para importar ideas de programas funcionales (porque FP es cool!) pero es muy difícil hacer bastante, ya que C# es y será siempre un imperativo lenguaje de programación.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top