205

閉鎖とは?それらは .NET に含まれていますか?

それらが .NET に存在する場合、それを説明するコード スニペット (できれば C#) を提供していただけますか?

4

12 に答える 12

273

このトピックに関する記事があります。(例がたくさんあります。)

本質的に、クロージャーは後で実行できるコードのブロックですが、最初に作成された環境を維持します。つまり、クロージャーを作成したメソッドのローカル変数などをその後も使用できます。メソッドの実行が終了しました。

クロージャの一般的な機能は、匿名メソッドとラムダ式によって C# に実装されています。

匿名メソッドを使用した例を次に示します。

using System;

class Test
{
    static void Main()
    {
        Action action = CreateAction();
        action();
        action();
    }

    static Action CreateAction()
    {
        int counter = 0;
        return delegate
        {
            // Yes, it could be done in one statement; 
            // but it is clearer like this.
            counter++;
            Console.WriteLine("counter={0}", counter);
        };
    }
}

出力:

counter=1
counter=2

ここで、CreateAction によって返されたアクションはまだカウンター変数にアクセスでき、CreateAction 自体が終了したにもかかわらず、カウンター変数を実際にインクリメントできることがわかります。

于 2009-01-09T16:04:44.463 に答える
23

C# が Closure を実装する方法に興味がある場合は、「I know the answer (its 42) blog」をお読みください。

コンパイラはバックグラウンドでクラスを生成して、無名メソッドと変数 j をカプセル化します。

[CompilerGenerated]
private sealed class <>c__DisplayClass2
{
    public <>c__DisplayClass2();
    public void <fillFunc>b__0()
    {
       Console.Write("{0} ", this.j);
    }
    public int j;
}

関数の場合:

static void fillFunc(int count) {
    for (int i = 0; i < count; i++)
    {
        int j = i;
        funcArr[i] = delegate()
                     {
                         Console.Write("{0} ", j);
                     };
    } 
}

それを次のように変えます:

private static void fillFunc(int count)
{
    for (int i = 0; i < count; i++)
    {
        Program.<>c__DisplayClass1 class1 = new Program.<>c__DisplayClass1();
        class1.j = i;
        Program.funcArr[i] = new Func(class1.<fillFunc>b__0);
    }
}
于 2011-08-22T03:47:08.063 に答える
12

クロージャーは、元のスコープの変数値を保持する関数値です。C# では、これらを匿名デリゲートの形式で使用できます。

非常に単純な例として、次の C# コードを取り上げます。

    delegate int testDel();

    static void Main(string[] args)
    {
        int foo = 4;
        testDel myClosure = delegate()
        {
            return foo;
        };
        int bar = myClosure();

    }

最後に、bar が 4 に設定され、myClosure デリゲートを渡して、プログラムの他の場所で使用できます。

クロージャは、遅延実行やインターフェイスの簡素化など、多くの便利なことに使用できます。LINQ は主にクロージャを使用して構築されています。ほとんどの開発者にとって最も便利な方法は、動的に作成されたコントロールにイベント ハンドラーを追加することです。クロージャーを使用すると、データを別の場所に保存するのではなく、コントロールがインスタンス化されたときに動作を追加できます。

于 2009-01-09T16:04:10.657 に答える
10
Func<int, int> GetMultiplier(int a)
{
     return delegate(int b) { return a * b; } ;
}
//...
var fn2 = GetMultiplier(2);
var fn3 = GetMultiplier(3);
Console.WriteLine(fn2(2));  //outputs 4
Console.WriteLine(fn2(3));  //outputs 6
Console.WriteLine(fn3(2));  //outputs 6
Console.WriteLine(fn3(3));  //outputs 9

クロージャーは、それが作成された関数の外部に渡される無名関数です。作成された関数から使用される変数を保持します。

于 2009-01-09T16:09:13.003 に答える
4

これは、JavaScriptの同様のコードから作成したC#の不自然な例です。

public delegate T Iterator<T>() where T : class;

public Iterator<T> CreateIterator<T>(IList<T> x) where T : class
{
        var i = 0; 
        return delegate { return (i < x.Count) ? x[i++] : null; };
}

だから、ここに上記のコードを使用する方法を示すいくつかのコードがあります...

var iterator = CreateIterator(new string[3] { "Foo", "Bar", "Baz"});

// So, although CreateIterator() has been called and returned, the variable 
// "i" within CreateIterator() will live on because of a closure created 
// within that method, so that every time the anonymous delegate returned 
// from it is called (by calling iterator()) it's value will increment.

string currentString;    
currentString = iterator(); // currentString is now "Foo"
currentString = iterator(); // currentString is now "Bar"
currentString = iterator(); // currentString is now "Baz"
currentString = iterator(); // currentString is now null

それがいくらか役立つことを願っています。

于 2009-01-09T16:12:27.333 に答える
3

クロージャーは、それ自体の外側の (スタックの下から) 変数を参照するコードのチャンクであり、後で呼び出されたり実行されたりする可能性があります (イベントまたはデリゲートが定義され、将来の無期限の時点で呼び出される可能性がある場合など)。 )...コードのチャンクが参照する外部変数がスコープ外になる可能性があるため (そうでなければ失われる可能性があるため)、コードのチャンクによって参照される (クロージャーと呼ばれる) という事実は、ランタイムに「保持する」ように指示します。 "その変数は、コードのクロージャー チャンクで不要になるまで、スコープ内にあります...

于 2009-01-09T16:09:24.140 に答える
2

基本的に、クロージャは、関数の引数として渡すことができるコードのブロックです。C#は、匿名のデリゲートの形式でクロージャをサポートします。

簡単な例を次に示します
。List.Findメソッドは、コードの一部(クロージャー)を受け入れて実行し、リストのアイテムを検索できます。

// Passing a block of code as a function argument
List<int> ints = new List<int> {1, 2, 3};
ints.Find(delegate(int value) { return value == 1; });

C#3.0構文を使用すると、次のように記述できます。

ints.Find(value => value == 1);
于 2009-01-09T16:13:10.950 に答える
-1

クロージャーは、関数内で定義された関数であり、そのローカル変数とその親にアクセスできます。

public string GetByName(string name)
{
   List<things> theThings = new List<things>();
  return  theThings.Find<things>(t => t.Name == name)[0];
}

そのため、find メソッド内の関数です。

 t => t.Name == name

スコープ t 内の変数と、親スコープ内の変数名にアクセスできます。find メソッドによってデリゲートとして実行されますが、別のスコープからまとめて実行されます。

于 2009-01-09T16:09:03.547 に答える