Подтвердить что ты не робот

Предупреждение ReSharper - доступ к модифицированному закрытию

У меня есть следующий код:

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

Обратите внимание, что account.AccountStatus - это перечисление типа ACCOUNTSTATUS. Во второй строке ReSharper дает мне предупреждение "Доступ к модифицированному закрытию" для acctStatus. Когда я делаю рекомендуемую операцию, скопируйте ее в локальную переменную, она изменит код на следующее:

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

Почему это лучше или предпочтительнее того, что у меня было изначально?

ИЗМЕНИТЬ

Он также рекомендует Wrap local variable в массиве, который создает:

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

Это кажется мне просто дураком.

4b9b3361

Ответ 1

Причиной предупреждения является то, что внутри цикла вы можете получить доступ к переменной, которая меняется. Тем не менее, "fix" на самом деле ничего не делает для вас в этом контексте без цикла.

Представьте, что у вас был цикл FOR, и внутри него было if, а объявление строки было вне него. В этом случае ошибка будет правильно идентифицировать проблему захвата ссылки на что-то нестабильное.

Пример того, чего вы не хотите:

string acctStatus

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

Проблема в том, что замыкание будет захватывать ссылку на acctStatus, но каждая итерация цикла изменит это значение. В этом случае было бы лучше:

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 local, note nodeType и HistoryItem crumb.ItemGuid, а не крошки [i].ItemGuid. Это гарантирует, что у моего закрытия не будет ссылок на элементы, которые будут меняться.

До использования локалей события будут запускаться с текущими значениями, а не с захваченными значениями, которые я ожидал.