【问题标题】:C# Access to modified closureC# 访问修改后的闭包
【发布时间】:2011-07-28 10:50:21
【问题描述】:
public virtual void OnRegistrationJoin(RegistrationJoinEventArgs e)
{
    foreach (Mobile member in e.Team)
    {
        member.SendMessage(1161, "You join the {0}.", EventFullName);

        if (e.Team.Count > 1)
        {
            Joinees.Remove(member);
            member.SendMessage(1161, "Your team formation is:");

            int i = 0;

            foreach (Mobile parter in e.Team.Where(partner => partner != member).ToList())
            {
                member.SendMessage(1150, "{0}: {1}.", ++i, partner.Name);
            }
        }
    }

    Members.Add(e.Team);
}

我收到 resharper 发出的“访问修改后的闭包”警告,我想知道这段代码有什么问题,因为我在内部循环中所做的只是发送一条消息?

【问题讨论】:

标签: c# resharper


【解决方案1】:

问题出在:

e.Team.Where(partner => partner != member)

变量member 是对外部作用域中member 变量的直接引用。虽然您在上面的代码中可能对此没有问题,但当您在多个线程上运行代码或没有立即在 Where 方法中评估 lambda(例如,使用 @987654325 @ 而不是 IEnumerable)。

这是一个问题的原因是 C# 生成了一个方法,然后作为委托传递给Where。该方法需要直接访问memeber。如果要将引用分配给另一个变量,如下所示:

var m = member;
// ...
e.Team.Where(partner => partner != m);

然后 C# 可以在称为“闭包”的构造中“捕获”该值并将其传递给生成的方法。这将确保当 member 更改时,您将其传递给 Where 时所期望的值不会更改。

【讨论】:

  • +1:最终,我认为对大多数人来说最大的问题是延迟执行。
  • 通过 var closureMember = member 轻松修复。并在 LINQ 中使用closureMember。
  • @Femaref - 但问题在于不断变化的引用,而不是变化的实例。
  • 当然是。但是您的 closureMember 的行为将完全相同 - 每次迭代,它都会更改为当前对象。如果他不直接使用查询而是延迟查询,则只会使用 foreach 序列的最后一个成员。
  • 哦,您的意思是在 lambda 之外分配 closureMember。当然,这会奏效。对不起。
【解决方案2】:

resharper 抱怨的部分是e.Team.Where(partner => partner != member).ToList(),因为引用的member 将被更改。在这种情况下,这不是问题,但在其他情况下,这可能是一个问题。

注意:您不必使用ToList(),这会强制对IEnumerable<T> 进行热切评估。只需遍历e.Team.Where(partner => partner != member)

【讨论】:

    【解决方案3】:

    我想member.SendMessage 可以修改member

    这是修改 lambda 中使用的封闭变量

    【讨论】:

    • 并非如此。问题是外部 foreach 循环修改了 lambda 中绑定的 member 变量。
    猜你喜欢
    • 2011-09-29
    • 2010-09-23
    • 2015-11-08
    • 2012-09-14
    • 2010-12-13
    • 1970-01-01
    • 2016-05-31
    • 2011-12-18
    相关资源
    最近更新 更多