Frage

Ich habe den folgenden Code:

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

Da der Compiler das Präfix Variable mit einem Verschluss „NEU: Brownie“ ersetzt. Wird auf die Konsole gedruckt

Gibt es eine einfache Möglichkeit, die Compiler von Anheben des Präfix Variable, während immer noch unter Verwendung eines Lambda-Ausdruck zu verhindern? Ich möchte eine Art und Weise meine Func Arbeit machen identisch mit:

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

Der Grund, warum ich brauche das ist, ich mag den resultierenden Delegaten serialisiert. Wenn das Präfix Variable in einer nicht-serializable Klasse ist, wird die obige Funktion nicht serialisiert werden.

Der einzige Weg, um dies ich im Moment sehen kann, ist eine neue serializable Klasse zu erstellen, die die Zeichenfolge als Member-Variable speichert und haben die Zeichenfolge prepend Methode:

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

Mit Hilfsklasse:

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

Dies scheint eine Menge zusätzlicher Arbeit der Compiler zu erhalten zu sein „stumm“.

War es hilfreich?

Lösung

Ich sehe das zugrunde liegende Problem jetzt. Es ist tiefer, als ich zuerst dachte. Grundsätzlich ist die Lösung ist, den Ausdrucksbaum zu ändern, bevor es serialisiert, indem alle Teilbäume ersetzt, die mit konstanten Knoten von den Parametern abhängen. Dies wird offenbar „funcletization“ genannt. Es gibt eine Erklärung davon hier .

Andere Tipps

Just another Verschluss machen ...

Sagen Sie, so etwas wie:

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

havn't es noch nicht getestet, wie ich Zugriff auf VS nicht im Moment, aber in der Regel, das ist, wie ich ein solches Problem zu lösen.

Lambdas automatisch ‚saugen‘ in lokalen Variablen, ich fürchte, das ist einfach, wie sie definitionsgemäß arbeiten.

Das ist ein ziemlich häufiges Problem heißt Variablen unbeabsichtigt durch einen Verschluss modifiziert wird - eine weit einfachere Lösung ist einfach zu gehen:

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

Wenn Sie ReSharper verwenden wird es tatsächlich die Stellen im Code identifizieren, wo Sie sind in Gefahr verursachen unerwartete Nebenwirkungen wie diese -. So, wenn die Datei „alle grünen“ Ihr Code sollte in Ordnung sein

ich denke, in gewisser Weise wäre es schön gewesen, wenn wir etwas syntaktischen Zucker haben diese Situation zu bewältigen und so konnten wir es als d.h Einzeiler geschrieben haben.

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

Wenn einige Präfixoperator der Wert der Variablen bewirken würde die Konstruktion die anonyme Delegaten / Funktion ausgewertet, bevor werden.

ich das Problem jetzt: das Lambda-bezieht sich auf die Klasse enthält, die nicht serialisierbar sein könnten. Dann tun Sie etwas wie folgt aus:

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

(Beachten Sie das Schlüsselwort static.) Dann wird die Lambda nicht enthalten Klasse muss verweisen.

Es gibt bereits einige Antworten hier zu erläutern, wie Sie das Lambda vermeiden können „Heben“ Ihre Variable. Leider ist das nicht die zugrunde liegende Problem lösen. Nicht in der Lage, die Lambda-serialisiert nichts mit dem Lambda mit „angehoben“ Ihren Variable zu tun hat. Wenn die Lambda-Ausdruck eine Instanz einer nicht-serialize Klasse muss zu berechnen, macht es durchaus Sinn, dass es nicht serialisiert werden können.

Je nachdem, was Sie tatsächlich versuchen zu tun (ich kann nicht ganz aus Ihrem Post entscheiden), eine Lösung wäre, die nicht serialisierbaren Teil des Lambda außen zu bewegen.

Zum Beispiel statt:

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

Verwendung:

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

Was ist das

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

Wie wäre:

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

Nun, wenn wir Gonna Vortrag über „Probleme“ hier sind, kommen lambdas aus der funktionalen Programmierung Welt und in einem rein funktionalen Programmierung Langauge, gibt es keine Zuweisungen und so würde Ihr Problem nie entstehen, weil Wert des Präfix nie ändern könnte. Ich verstehe, C # denkt, dass es cool ist, Ideen aus funktionale Programme zu importieren (weil FP ist cool!), Aber es ist sehr schwer, es ziemlich zu machen, weil C # und wird immer eine imperative Programmiersprache sein.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top