IDE /编译器如何知道lambda表达式是立即执行的,而不是保存以供以后使用?

时间:2018-01-11 16:42:19

标签: c# lambda compiler-warnings code-analysis

编译器(我在这里松散地使用这个词,我的VS,Roslyn,R#工具链中的某个人)知道我何时访问lambda表达式中的变量,如果以后执行该变量可能已经被处理掉了。这是一个很棒的功能和欢迎警告。但是,有时我知道lambda表达式将立即执行 而不保存供以后使用,因此这个警告是错误的。

有人可能会争辩说,编译器无法知道lambda表达式是执行还是保留供以后使用。有趣的事实:确实如此。如果我在LinQ语句中使用它,那么当我通过例如调用.ToList()立即实现该事物时,我会得到一条波浪形的警告线,除了。然后警告就消失了。所以编译器知道是否一次使用lambda并丢弃,或保留供以后使用。

使用波浪线进入处置封闭物:

enter image description here

我如何向我的工具链解释我的方法(采用lambda的.ThrownFrom() 实际上是实际上是立即执行它而不保留对它的引用?

我可以通过我可以设置的属性,模式或其他东西获得与实现LinQ相同的效果,还是一些特定于LinQ的硬编码编译器魔法?

我的忽略课程为例:

public static class Ignore<TException> where TException : Exception
{
    public static void ThrownFrom([NotNull] Action action)
    {
        if (action is null)
        {
            throw new ArgumentNullException(nameof(action));
        }

        try
        {
            action();
        }
        catch (TException ex)
        {
            // ignore
        }
    }
}

2 个答案:

答案 0 :(得分:4)

  

有人可能会争辩说,编译器无法知道lambda表达式是执行还是保留供以后使用。

这样的论证是正确的。它假设任何lambda都可以在应用程序的未来任意点调用,并相应地编译代码。

  

有趣的事实:确实如此。如果我在LinQ语句中使用它,我会得到波浪形警告线,除非我立即通过例如调用.ToList()实现该事物。然后警告就消失了。因此,编译器知道lambda是否被使用一次并被丢弃,或者保留供以后使用。

不,编译器不会这样做。一些代码分析工具试图确定给定的lambda是否可以在给定范围之外调用,但是他们无法知道。在一般情况下,不可能知道,但在某些特定情况下,您有时可以知道。他们简单地用硬编码了一堆特定的案例,其中他们不知道是否立即调用lambda(在这种情况下,他们只是硬编码到代码分析工具中的某些框架的事实方法会立即调用提供的委托,其他方法会暂时调用它。鉴于此,他们有时会犯错误,因为有许多方法,他们根本无法知道代表是否被保留。

  

我如何向我的工具链解释我的方法(带有lambda的.ThrownFrom())实际上是在执行它并且不保留对它的引用?

您可以查看您使用的任何代码分析工具的文档。他们可能会提供这样做的方法,而他们可能不这样做。

答案 1 :(得分:2)

事实证明,来自@Servy的答案是绝对正确的(并不奇怪),我的问题的解决方案实际上是在我的代码分析工具的帮助页面上发出警告(令人惊讶):

如果您正在使用Resharper(生成此警告),您可以使用[InstantHandle]属性来表示此lambda表达式实际上是立即处理的。

所以:

public static class Ignore<TException> where TException : Exception
{
    public static void ThrownFrom([InstantHandle][NotNull] Action action)
    {
        if (action is null)
        {
            throw new ArgumentNullException(nameof(action));
        }

        try
        {
            action();
        }
        catch (TException ex)
        {
            // ignore
        }
    }
}