8

私は次のことをかなり奇妙に感じました。繰り返しになりますが、私は主に動的言語でクロージャーを使用してきましたが、これは同じ「バグ」の疑いがあるべきではありません。以下は、コンパイラを不幸にします:

VoidFunction t = delegate { int i = 0; };

int i = 1;

それは言います:

「i」という名前のローカル変数をこのスコープで宣言することはできません。これは、「子」スコープで他の何かを示すために既に使用されている「i」に別の意味を与えるためです。

したがって、これは基本的に、デリゲート内で宣言された変数が宣言された関数のスコープを持つことを意味します。私が期待していたものとはまったく異なります。私は関数を呼び出そうとさえしていません。少なくとも Common Lisp には、変数を本当にローカルにしたい場合は、変数に動的な名前を付ける必要があると言う機能があります。これは、リークしないマクロを作成する場合に特に重要ですが、このような場合にも役立ちます。

だから、この問題を回避するために他の人が何をしているのか疑問に思っていますか?

明確にするために、デリゲートで宣言する変数がデリゲートの後に宣言された変数に干渉しないソリューションを探しています。そして、デリゲートの前に宣言された変数を引き続きキャプチャできるようにしたいと考えています。

4

6 に答える 6

9

匿名メソッド (およびラムダ) がローカル変数と包含メソッドでスコープされたパラメーターを使用できるようにするには、そのようにする必要があります。

回避策は、変数に別の名前を使用するか、通常のメソッドを作成することです。

于 2009-01-01T12:33:02.430 に答える
3

匿名関数によって作成される「クロージャ」は、他の動的言語で作成されるものとは多少異なります(例としてJavascriptを使用します)。

function thing() {
    var o1 = {n:1}
    var o2 = {dummy:"Hello"}
    return function() { return o1.n++; }
}

var fn = thing();
alert(fn());
alert(fn());

この小さなJavaScriptのチャンクは、1、2の順に表示されます。無名関数は、スコープチェーンに存在するため、o1変数にアクセスできます。ただし、無名関数には完全に独立したスコープがあり、別のo1変数を作成して、スコープチェーンのさらに下にある他の変数を非表示にすることができます。また、チェーン全体のすべての変数が残っているため、fn varialbeが関数参照を保持している限り、o2はオブジェクト参照を保持して存在し続けることに注意してください。

ここで、C#無名関数と比較してください:-

class C1 { public int n {get; set;} }
class C2 { public string dummy { get; set; } }

Func<int> thing() {
   var o1 = new C1() {n=1};
   var o2 = new C2() {dummy="Hello"};
   return delegate { return o1.n++; };
}
...
Func<int> fn = thing();
Console.WriteLine(fn());
Console.WriteLine(fn());

foreachこの場合、無名関数は、他の関数内の{}ブロックのコードで変数宣言が使用される( 、、ifなどで使用される)以上に、真に独立したスコープを作成していません。

したがって、同じルールが適用され、ブロック外のコードはブロック内で宣言された変数にアクセスできませんが、識別子を再利用することもできません。

クロージャは、無名関数が作成された関数の外部に渡されたときに作成されます。Javascriptの例との違いは、無名関数によって実際に使用される変数のみが残るため、この場合、o2によって保持されるオブジェクトは物事が完了するとすぐにGCで利用できるようになります。

于 2009-01-01T13:26:09.513 に答える
1

また、次のようなコードからCS0136を取得します。

  int i = 0;
  if (i == 0) {
    int i = 1;
  }

「i」の2番目の宣言の範囲は明確であり、C++のような言語には牛肉がありません。しかし、C#言語の設計者はそれを禁止することにしました。上記のスニペットを考えると、それはまだ悪い考えだと思いますか?たくさんの余分なコードを投入すると、このコードをしばらく見つめてもバグはわかりません。

回避策は簡単で簡単です。別の変数名を考え出すだけです。

于 2009-01-01T13:56:25.680 に答える
0

実際、このエラーは匿名のデリゲートやラムダ式とは何の関係もないようです。次のプログラムをコンパイルしようとすると...

using System;

class Program
{
    static void Main()
    {
        // Action t = delegate
        {
            int i = 0;
        };

        int i = 1;
    }
}

...行にコメントするかどうかに関係なく、まったく同じエラーが発生します。エラーヘルプは、非常によく似たケースを示しています。プログラマーが2つの変数を混同する可能性があるという理由で、両方のケースを許可しないのは合理的だと思います。

于 2009-01-01T13:57:27.893 に答える
0

私の記憶が正しければ、コンパイラは匿名メソッドで参照される外部変数のクラス メンバーを作成して、これを機能させます。

回避策は次のとおりです。

class Program
    {
        void Main()
        {
            VoidFunction t = RealFunction;
            int i = 1;
        }
        delegate void VoidFunction();
        void RealFunction() { int i = 0; }
    } 
于 2009-01-01T12:43:52.487 に答える
0

これは、デリゲートがデリゲートの外部の変数を参照できるためです。

int i = 1;
VoidFunction t = delegate { Console.WriteLine(i); };
于 2009-01-01T12:34:59.617 に答える