web-dev-qa-db-ja.com

ReSharper警告-変更されたクロージャーへのアクセス

私は次のコードを持っています:

string acctStatus = account.AccountStatus.ToString();
if (!SettableStatuses().Any(status => status == acctStatus))
    acctStatus = ACCOUNTSTATUS.Pending.ToString();

Account.AccountStatusはACCOUNTSTATUSタイプの列挙型であることに注意してください。 2行目で、ReSharperはacctStatusに対して「変更されたクロージャーへのアクセス」という警告を表示しています。推奨される操作Copy to local variableを実行すると、コードが次のように変更されます。

string acctStatus = realAccount.AccountStatus.ToString();
string s = acctStatus;
if (!SettableStatuses().Any(status => status == s))
    acctStatus = ACCOUNTSTATUS.Pending.ToString();

なぜこれが私が最初に持っていたものよりも優れているか、または好ましいのですか?

[〜#〜]編集[〜#〜]

また、ローカル変数をarrayでラップすることをお勧めします。これにより、次のようになります。

string[] acctStatus = {realAccount.AccountStatus.ToString()};
if (!SettableStatuses().Any(status => status == acctStatus[0]))
    acctStatus[0] = ACCOUNTSTATUS.Pending.ToString();

これは実に奇妙に思えます。

37
Matt Grande

警告の理由は、ループ内で変更中の変数にアクセスしている可能性があるためです。ただし、この「修正」は、この非ループコンテキストでは実際には何も行いません。

FORループがあり、ifがその中にあり、文字列宣言がその外側にあったとします。その場合、エラーは不安定なものへの参照を取得する問題を正しく識別します。

あなたが望まないものの例:

string acctStatus

foreach(...)
{
  acctStatus = account.AccountStatus[...].ToString();
  if (!SettableStatuses().Any(status => status == acctStatus))
      acctStatus = ACCOUNTSTATUS.Pending.ToString();
}

問題は、クロージャーがacctStatusへの参照を取得することですが、ループを繰り返すたびにその値が変更されます。 thatの場合、より良いでしょう:

foreach(...)
{
  string acctStatus = account.AccountStatus[...].ToString();
  if (!SettableStatuses().Any(status => status == acctStatus))
      acctStatus = ACCOUNTSTATUS.Pending.ToString();
}

変数のコンテキストはループであるため、変数をローカルコンテキスト内に移動したため(forループ)、毎回新しいインスタンスが作成されます。

この推奨事項は、Resharperによるそのコードの解析のバグのように聞こえます。ただし、多くの場合、これは有効な懸念事項です(最初の例では、クロージャーでのキャプチャーにもかかわらず参照が変化しています)。

私の経験則は、疑わしいときは地元の人を作ることです。

これが私が噛まれた実世界の例です:

        menu.MenuItems.Clear();
        HistoryItem[] crumbs = policyTree.Crumbs.GetCrumbs(nodeType);

        for (int i = crumbs.Length - 1; i > -1; i--) //Run through items backwards.
        {
            HistoryItem crumb = crumbs[i];
            NodeType type = nodeType; //Local to capture type.
            MenuItem menuItem = new MenuItem(crumb.MenuText);
            menuItem.Click += (s, e) => NavigateToRecord(crumb.ItemGuid, type);
            menu.MenuItems.Add(menuItem);
        }

私はnodeTypeタイプをローカルでキャプチャし、nodeTypeとHistoryItem crumb.ItemGuidをキャプチャし、crumbs [i] .ItemGuidではないことに注意してください。これにより、クロージャに変更されるアイテムへの参照が含まれないようになります。

ローカルを使用する前は、イベントは、私が期待したキャプチャされた値ではなく、現在の値でトリガーされていました。

35
Godeke