8
static void Main(string[] args)
{
    var s = 3;

    Func<int, Func<int>> func = 
        x => () =>
    {
        return x;
    };

    var result1 = func(s);

    func = null;

    s = 5;

    var result2 = result1();

    Console.WriteLine(result2);


    Console.ReadKey();
}

私の理解では、xは実際には変数として宣言されていません。var x =3。代わりに、元の値を返す関数を返す外部関数に渡されます。これを返すときに、その値を記憶するためにxの周りにクロージャを作成します。その後、sを変更しても、効果はありません。

これは正しいですか?

(ちなみに出力は3ですが、期待通りです)。

編集:これがなぜだと思うのかについての図です

クロージャの例

x = 3が関数に渡され、xを返すだけの関数が返されます。しかし、xは内部関数には存在せず、その親のみであり、nullにすると、その親は存在しなくなります。内部関数が実行されるとき、xはどこに格納されますか?親からクロージャを作成する必要があります。

さらなる解明:

    int s = 0;

    Func<int, Func<int>> func = 
        x => () =>
    {
        return x;
    };

    for (s = 0; s < 5; s++)
    {
        var result1 = func(s);

        var result2 = result1();

        Console.WriteLine(result2);
    };

出力は0、1、2、3、4です

ただし、あなたの例では:

static void Main(string[] args)
{
    int s = 0;

    Func<int, Func<int>> func = 
        x => () =>
    {
        return s;
    };

    List<Func<int>> results = new List<Func<int>>();

    for (s = 0; s < 5; s++)
    {
         results.Add(func(s));
    };

    foreach (var b in results)
    {
        Console.WriteLine(b());
    }

    Console.ReadKey();
}

出力は555 5 5ですが、これは必要なものではありません。変数の値は取得されておらず、元のsへの参照が保持されているだけです。

この問題を回避するために、クロージャはjavascriptで正確に作成されます。

4

2 に答える 2

12

いいえ、これはのクロージャではありません。sデリゲートxを生成するラムダ式への引数にすぎません(実際にはに簡略化できます) x => () => x

        Func<int, Func<int>> func = x => () => x;

        var a = func(3);

        var b = func(5);

        Console.WriteLine(a());
        Console.WriteLine(b());

あなたは期待通り3に得るでしょう。5どちらの場合も、実際にxは他の時間から記憶していませんxが、外側のラムダの持続時間中 にローカルで閉じます。

基本的に、を呼び出すたびに、(渡されたものではなく)のローカル値をキャプチャx => () => xする新しいデリゲートが作成されます。() => xxs

使用sして渡し、変更しても、次のように3なり5ます。

        int s = 3;
        Func<int, Func<int>> func = x => () => x;

        var a = func(s);

        s = 5;

        var b = func(s);

        // 3 and 5 as expected
        Console.WriteLine(a());
        Console.WriteLine(b());

これで、すべての呼び出しで生成されるローカル関数のローカル変数をキャプチャします。したがって、より大きなラムダの呼び出しの間は持続しませんが、後でラムダで変更された場合はキャプチャされます。 x x

たとえば、代わりにこれがあったとしましょう:

        int s = 3;
        Func<int, Func<int>> func = x => 
            () => {
                      Func<int> result = () => x;
                      x = 10 * x;
                      return result;
                   };

        var a = func(s);

        s = 5;

        var b = func(s);

この場合、xanonymousメソッド内でのクロージャがより明白になります。x匿名メソッド内での変更は、その匿名メソッドへのローカルのクロージャに影響を与えるため、これを実行した結果は30と50になります。ただし、匿名メソッドに渡されたxローカルのみをキャプチャするため、これらは呼び出し間で引き継がれません。それを呼び出すためxに使用されるものではありません。s

したがって、要約すると、ダイアグラムと例では、次のようになります。*メインsはより大きなラムダ(ダイアグラムのfunc )に渡されます*匿名メソッドを生成するために呼び出されたときにfuncが閉じますx() => x

外側のラムダへの呼び出しが行われると(func)、クローザーはそのラムダに対してのみローカルであり、メインから何も閉じないため、クローザーが開始および終了します。

それは役に立ちますか?

于 2012-06-12T14:41:53.143 に答える
4

変化なし

Func<int, Func<int>> func = 
    x => () =>
{
    return x;
};

Func<int, Func<int>> func = 
    x => () =>
{
    return s;
};

そしてそれは変数を閉じsます。

于 2012-06-12T14:44:22.270 に答える