【问题标题】:ReSharper Warning - Access to Modified ClosureReSharper 警告 - 访问修改后的闭包
【发布时间】:2010-12-13 21:26:19
【问题描述】:

我有以下代码:

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();

为什么这比我原来的更好或更可取?

编辑

它还建议将局部变量包装在数组中,它会产生:

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

这对我来说似乎很古怪。

【问题讨论】:

标签: c# resharper warnings


【解决方案1】:

警告的原因是在循环内您可能正在访问一个正在更改的变量。但是,在这种非循环上下文中,“修复”并没有真正为您做任何事情。

想象一下,你有一个 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 类型,请注意 nodeType 和 HistoryItem crumb.ItemGuid,而不是 crumbs[i].ItemGuid。这确保我的闭包不会引用将要更改的项目。

在使用本地变量之前,事件将使用当前值触发,而不是我预期的捕获值。

【讨论】:

  • “问题是闭包会获取对 acctStatus 的引用,但每次循环迭代都会改变该值”——实际上,只要 Any 谓词评估它,它就会始终是 account.AccountStatus[...].ToString()(因为Any 在返回之前迭代枚举),所以我不确定这是一个很好的例子。我相信您提出的更好的建议将具有相同的行为。
  • 这是不正确的。差异的原因是在第一个示例中只分配了一个变量:acctStatus。 Resharper 担心此变量会在实际评估 Any() 之前发生变化。我的建议根本不会改变代码的操作(在任何更改发生之前对 Any() 进行评估),但明确表示我们希望为循环的每次迭代创建一个不同的变量 acctStatus 实例。如果您注意到我后面的示例,Resharper 担心的错误实际上会触发,因为我立即评估 lambda(减去本地变量)。
猜你喜欢
  • 1970-01-01
  • 2015-11-08
  • 2011-02-26
  • 2012-09-14
  • 2011-02-05
  • 1970-01-01
  • 2011-09-29
  • 2017-01-26
相关资源
最近更新 更多