IDE/Compiler 如何知道一个 lambda 表达式被立即执行,而不是被保存以备后用?

How does the IDE/Compiler know that a lambda expression is executed immediately and not saved for later use?

编译器(我在这里松散地使用这个词,我的 VS、Roslyn、R# 工具链中的某个人)知道我何时访问 lambda 表达式中的变量,如果稍后执行可能已经被释放。这是一个很棒的功能和一个受欢迎的警告。然而,有时我知道一个 lambda 表达式将立即执行并且不会保存以备后用,所以这个警告是错误的。

有人可能会争辩说,编译器无法知道 lambda 表达式是被执行还是保留以备后用。有趣的事实:确实如此。如果我在 LinQ 语句中使用它,我会收到波浪形的警告行,except,因为当我立即通过调用 .ToList() 实现该事物时。然后警告消失。因此编译器 知道 lambda 是使用一次并丢弃,还是保留以备后用。

访问带有波浪线的处置闭包:

我如何向我的工具链解释我的方法(接受 lambda 的 .ThrownFrom() 实际上是立即执行它并且不引用它?

我能否通过属性、模式或其他我可以设置的东西获得与具体化 LinQ 相同的效果,或者是 LinQ 特定的一些硬编码编译器魔法?


以我忽略class为例:

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
        }
    }
}

One might argue that the compiler cannot know whether the lambda expression is executed or kept for later use.

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

Fun fact: it does. If I use this in a LinQ statement, I get the squiggly warning line, except for when I materialize the thing immediately with for example a call to .ToList(). Then the warning vanishes. So the compiler knows if a lambda is used once and discarded, or kept for later use.

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

How can I explain to my toolchain that my method (the .ThrownFrom() that takes a lambda) is in fact executing it immediately and keeps no reference to it?

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

事实证明,@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
        }
    }
}