【问题标题】:Implicitly captured closures, ReSharper warning隐式捕获的闭包,ReSharper 警告
【发布时间】:2013-09-22 10:47:13
【问题描述】:

我通常知道“隐式捕获的闭包”是什么意思,但是,今天我遇到了以下情况:

public static void Foo (Bar bar, Action<int> a, Action<int> b, int c)
{
    bar.RegisterHandler(x => a(c)); // Implicitly captured closure: b
    bar.RegisterHandler(x => b(c)); // Implicitly captured closure: a
}

为什么我还要隐式捕捉其他动作?如果我评论这两行中的任何一行,另一行不会给我警告。有人知道 ReSharper 警告我有什么危险吗?

编辑:ReSharper 8.0.1

【问题讨论】:

标签: c# closures resharper


【解决方案1】:

我以前见过这个;它与在 lambda 的生命周期内保留的变量有关,因此如果它们很大,则会产生内存压力。

【讨论】:

  • 找到原始对话的链接devnet.jetbrains.com/thread/436232
  • 是的,但有趣的是为什么第一个 lambda 持有对 b 的引用,而第二个 lambda 持有对 a 的引用。
【解决方案2】:

这里的问题是,当您关闭一个变量时,幕后发生的事情是编译器创建一个新的未命名类型,为该类型提供一个实例字段,用于 在该块中关闭的每个变量 em>,为该代码块中的每个匿名方法提供一个方法,然后传递该对象的 单个实例

这意味着第一个委托的生命周期使该闭包对象保持活动状态,并且除了 a 之外,它在内部还引用了对象 b,反之亦然。

现在,在您的情况下,这不是问题,因为 Action 不是特别占用内存的东西,所以让它存活更长时间并不是真正的问题。

理论上,C# 团队可以确保在这种特殊情况下,可以为同一块中的每个闭包创建一个新的未命名类型,但他们选择不这样做,因为这会使常见情况变得更糟。

【讨论】:

  • 谢谢,我明白了。除了将两条线移动到不同的类型之外,我还能做些什么吗?
  • @D.R.好吧,正如我所说,在这种情况下,您几乎可以不在乎。如果它们是昂贵的对象并且能够更早地清除它们的内存很重要,那么你不能为此使用闭包。自己创建新的命名类型,使用方法和每个类型中每个操作的实例字段,在此方法中创建它们的实例等。闭包实际上不会让你做任何你不能做的事情,它们只是删除样板代码并使其变得更容易。
  • 您可以使用 ReSharper 的注释功能将 [InstantHandle] 属性添加到 RegisterHandler 方法。这告诉 ReSharper lambda 会立即执行,因此闭包的范围是有限的,并且 ReSharper 不会显示警告。
  • @citizenmatt 根据显示的名称和上下文,它们似乎没有立即执行。我的猜测是,它会一直保留代表,直到以后,可能是相当长的一段时间。这里的问题是,被关闭的对象不太可能很昂贵,并且会因延长其生命周期而遭受巨大损失。有可能,只是不太可能。这里重要的是,动作以及其中引用的任何对象的预期生命周期都不会比bar 短很多,或者不是特别占用内存。
  • 好点。应该说清楚的。当然,如果是这种情况,那么警告很有用,应该提示检查代码以查看此处延长生命周期是否合适。如果是这样,您可以放心地忽略警告,或者使用正确格式的 cmets 在本地禁用它 - 警告上的 alt+enter 将有助于生成警告。
【解决方案3】:

还有一个警告,我为此发疯了:

List<List<string>> allowed = AllowedSCACSwaps;
foreach (List<string> c in allowed.Where(c => c.Contains(scac)))
{
    csc = openCycles.FirstOrDefault(icsc => (icsc.CustomerCode == customerCode) && c.Contains(icsc.SCAC));
    if (null != csc)
    {
        return csc;
    }
}

说“customerCode 的隐式关闭”

string cc = customerCode.ToUpperInvariant();
string sc = scac.ToUpperInvariant();    List<List<string>> allowed = AllowedSCACSwaps;
    foreach (List<string> c in allowed.Where(c => c.Contains(sc)))
    {
        csc = openCycles.FirstOrDefault(icsc => (icsc.CustomerCode == cc) && c.Contains(icsc.SCAC));
        if (null != csc)
        {
            return csc;
        }
    }

没问题。

我发疯的原因是什么?

scac 和 customerCode 都是传递给方法的字符串。 但即使我用 cc 替换了 customerCode,我仍然收到同样的警告。

关闭实际上已经结束了 scac,但 Resharper 误报了它。

【讨论】:

  • 呸,代码也不支持粗体:(。警告出现在这里:allowed.Where(c => c.Contains(scac)))跨度>
  • 这不是误报。每个闭包的警告列出了在该闭包中实际未使用但无论如何都会被它捕获的变量。所以你应该在原始代码中得到两个警告:一个是关于customerCodeContains(scac) 警告,另一个是关于scacicsc.CustomerCode == customerCode 警告。
  • 除了关闭警告是第 3 行,而不是第 5 行。除非您说 foreach 的主体被转换为匿名方法调用,否则这对我来说没有意义。
  • 只看顶部的 sn-p,您应该在第 2 行收到“customerCode”警告,并在第 4 行收到“scac”警告,因为那是有问题的 lambda出现。
猜你喜欢
  • 2012-10-08
  • 2012-11-17
  • 2016-08-05
  • 2013-06-10
  • 1970-01-01
  • 1970-01-01
  • 2010-12-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多