2
int count = itemsToValidate.Count;
foreach(var item in itemsToValidate)
{
   item.ValidateAsync += (x, y) => this.HandleValidate(ref count);
}

private void HandleValidate(ref int x)
{
  --x;
  if (x == 0)
  {
       // All items are validated.
  }
}

上記のコードについて、resharperは「変更されたクロージャへのアクセス」と不平を言いました。それをオブジェクトのタイプに変更した場合、それは行われません。私がrefを通り過ぎているのに、なぜこれが閉鎖なのですか?

4

1 に答える 1

4

これは常に起こります

ReSharperはcount、「検証完了」イベントハンドラーとして割り当てているラムダによって暗黙的にキャプチャされ、ラムダが作成されたとき(つまり、イベントハンドラーを割り当てたとき)とその値が変わる可能性があることを警告しています。呼び出されます。これが発生した場合、ラムダは直感的に期待できる値を認識しません。

例:

int count = itemsToValidate.Count;
foreach(var item in itemsToValidate)
{
   item.ValidateAsync += (x, y) => this.HandleValidate(ref count);
}

// afterwards, at some point before the handlers get invoked:
count = 0;

この場合、ハンドラーはcountの値を--0の代わりに読み取ります。これitemsToValidate.Countは「明らか」と呼ばれる場合がありますが、ラムダの仕組みに精通していない多くの開発者にとっては驚くべきことであり、直感に反します。

そして、私たちは通常このようにそれを解決します

「R#をシャットダウン」する通常の解決策は、キャプチャされた変数を内部スコープに移動することです。この場合、アクセスしにくくなり、ラムダが評価されるまでR#を変更できないことを証明できます。

int count = itemsToValidate.Count;
foreach(var item in itemsToValidate)
{
   int inner = count; // this makes inner impossible to modify
   item.ValidateAsync += (x, y) => this.HandleValidate(ref inner);
}

// now this will of course not affect what the lambdas do
count = 0;

しかし、あなたのケースは特別です

あなたの特定のケースは、特にこの動作が必要な比較的まれなケースであり、上記のトリックを使用すると、実際にはプログラムが正しく動作しなくなります(同じカウントを指すようにキャプチャされた参照が必要です)。

正しい解決策:R#が認識する特別な行のコメントを使用して、この警告を無効にします。

于 2012-09-22T19:01:43.883 に答える