質問

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

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

int i = 1;

言う:

  

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

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

では、この問題を回避するために他の人が何をしているのだろうか?

明確にするために、デリゲートで宣言した変数がデリゲートの で宣言された変数と干渉しない解決策を探しています。そして、デリゲートの前に宣言された変数をキャプチャできるようにしたいです。

役に立ちましたか?

解決

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

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

他のヒント

「クロージャ」無名関数によって作成されたものは、他の動的言語で作成されたものとは多少異なります(例として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変数にアクセスできます。ただし、匿名関数には完全に独立したスコープがあり、別のo1変数を作成して、スコープチェーンのさらに下にある他の変数を非表示にすることができます。また、チェーン全体のすべての変数が残っていることに注意してください。したがって、fn変数が関数参照を保持している限り、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の例との違いは、匿名関数で実際に使用される変数のみが残るため、この場合、オブジェクトはby o2は、事が完了するとすぐにGCで利用可能になります。

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

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

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

回避策は簡単で簡単です。別の変数名を考えてください。

デリゲートはデリゲートの外部の変数を参照できるためです:

int i = 1;
VoidFunction t = delegate { Console.WriteLine(i); };

正しく覚えていれば、コンパイラは、この動作を実現するために、匿名メソッドで参照される外部変数のクラスメンバーを作成します。

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

class Program
    {
        void Main()
        {
            VoidFunction t = RealFunction;
            int i = 1;
        }
        delegate void VoidFunction();
        void RealFunction() { int i = 0; }
    } 

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

using System;

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

        int i = 1;
    }
}

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top