7

私は次のコードを持っています:

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

コンパイラがプレフィックス変数をクロージャに置き換えるため、「NEW:brownie」がコンソールに出力されます。

ラムダ式を使用しているときにコンパイラーがプレフィックス変数を持ち上げないようにする簡単な方法はありますか?Funcを次のように機能させる方法が必要です。

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

これが必要な理由は、結果のデリゲートをシリアル化したいからです。プレフィックス変数がシリアル化できないクラスにある場合、上記の関数はシリアル化されません。

現時点で私が見ることができるこれを回避する唯一の方法は、文字列をメンバー変数として格納し、文字列の先頭に追加するメソッドを持つ新しいシリアル化可能なクラスを作成することです。

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

ヘルパークラスあり:

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

これは、コンパイラを「ダム」にするための多くの余分な作業のようです。

4

9 に答える 9

8

根本的な問題が見えてきました。最初に思ったよりも奥が深いです。基本的な解決策は、パラメーターに依存しないすべてのサブツリーを定数ノードに置き換えることにより、シリアル化する前に式ツリーを変更することです。これは明らかに「関数化」と呼ばれます。ここに 説明 があります.

于 2008-09-21T09:39:54.803 に答える
2

別の閉鎖を行うだけです...

次のように言います。

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

現時点ではVSにアクセスできないため、まだテストしていませんが、通常、これがそのような問題を解決する方法です。

于 2008-09-21T09:44:01.173 に答える
1

ラムダはローカル変数に自動的に「吸い込まれ」ます。定義上、ラムダは単にそのように機能するのではないかと思います。

于 2008-09-21T08:28:32.227 に答える
0

これは非常に一般的な問題です。つまり、変数が意図せずにクロージャによって変更されます。はるかに簡単な解決策は次のとおりです。

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

resharperを使用している場合、このような予期しない副作用を引き起こすリスクがあるコード内の場所を実際に識別します。したがって、ファイルが「すべて緑色」の場合、コードは問題ありません。

ある意味で、この状況を処理するためのシンタックスシュガーがあれば、ワンライナーとして書くことができればよかったと思います。

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

一部のプレフィックス演算子により、匿名のデリゲート/関数を作成する前に変数の値が評価される場合。

于 2008-09-21T08:31:03.580 に答える
0

ここには、変数を「持ち上げる」ラムダを回避する方法を説明するいくつかの回答が既にあります。残念ながら、それはあなたの根本的な問題を解決しません。ラムダをシリアル化できないことは、ラムダが変数を「持ち上げた」こととは何の関係もありません。ラムダ式が計算のために非シリアル化クラスのインスタンスを必要とする場合、それをシリアル化できないことは完全に理にかなっています。

あなたが実際に何をしようとしているのかにもよりますが (あなたの投稿からは判断できません)、解決策は、ラムダのシリアル化できない部分を外側に移動することです。

たとえば、次の代わりに:

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

使用する:

NonSerializable nonSerializable = new NonSerializable();
string prefix = nonSerializable.ToString();
Func<string, string> prependAction = (x => prefix + x);
于 2008-09-21T08:38:59.940 に答える
0

問題が発生しました。ラムダは、シリアル化できない可能性のあるクラスを含んでいます。次に、次のようにします。

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

( static キーワードに注意してください。) その場合、ラムダは含まれているクラスを参照する必要はありません。

于 2008-09-21T09:11:07.837 に答える
-1

これはどうですか

string prefix = "OLD:";
string _prefix=prefix;
Func<string, string> prependAction = (x => _prefix + x);
prefix = "NEW:";
Console.WriteLine(prependAction("brownie"));
于 2008-09-21T08:26:47.490 に答える
-1

どうですか:

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

于 2008-09-21T08:27:28.423 に答える
-1

ここで「問題」について話す場合、ラムダは関数型プログラミングの世界から来ており、純粋な関数型プログラミング言語には代入がないため、接頭辞の値は決して変更できないため、問題は発生しません。C# が関数型プログラムからアイデアをインポートするのはクールだと考えていることは理解しています (FPクールだからです!)。

于 2008-09-21T08:48:09.247 に答える