为什么C#编译器甚至没有警告无休止的递归?

B. *_*non 6 .net c# recursion infinite-loop compiler-warnings

遗留应用程序在启动时处于无限循环中; 我不知道为什么/如何(代码混淆竞赛候选人),但关于被反复调用的方法(从其他几种方法调用),我想,"我想知道是否有一种调用此方法的方法还在调用另一个也叫它的方法?"

我想:"不,编译器能够解决这个问题,而不是允许它,或者至少发出警告!"

所以我创建了一个简单的应用来证明这种情况:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        method1();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        method2();
    }

    private void method1()
    {
        MessageBox.Show("method1 called, which will now call method2");
        method2();
    }

    private void method2()
    {
        MessageBox.Show("method2 called, which will now call method1");
        // Note to self: Write an article entitled, "Copy-and-Paste Considered Harmful"
        method1();
    }
}
Run Code Online (Sandbox Code Playgroud)

...但不是!它编译得很好.为什么编译器不会将此代码标记为最可疑?如果任何一个按钮被捣碎,你就永远不会降落!

好的,有时您可能需要无限循环(起搏器代码等),但我认为应该发出警告.

Zot*_*tta 12

  1. 正如你所说,有时人们想要无限循环.并且.net的jit-compiler支持tailcall优化,所以你可能甚至不会像你那样获得无限递归的堆栈溢出.

  2. 对于一般情况,在有限时间内预测程序是否将在某个时刻终止或陷入无限循环是不可能的.这被称为停止问题.所有编译器都可能找到一些特殊情况,很容易决定.


Oli*_*bes 9

这不是无限循环,而是无休止的递归.这更糟糕,因为它们可能导致堆栈溢出.除非您编写恶意软件,否则大多数语言都不需要无限递归.然而,无尽的循环通常是有意的.服务通常以无限循环运行.

为了检测这种情况,编译器必须按照方法调用来分析代码; 但是C#编译器将此过程限制为当前方法中的直接代码.例如,这里可以跟踪未初始化或未使用的变量,并且可以检测不可达的代码.在编译速度和静态分析和优化的深度之间需要权衡.

此外,几乎不可能知道程序员的真实意图.

想象一下,你写了一个完全合法的方法.突然因为你从另一个地方调用这个方法,你的编译器会抱怨并告诉你你的方法不再合法.我已经可以在SO上看到大量的帖子了:"我昨天编译的方法.今天它不再编译了.但我没有改变它".

  • 某些可能看起来很糟糕的情况可能是有意的,在某些情况下仍然有意义.编译器在人类意义上并不智能,它只是一台机器. (3认同)
  • 如果编译器开始抱怨糟糕的代码,世界各地的开发计算机都会因突然发出大量警告信息而疲惫不堪.:) (3认同)

Cro*_*ono 7

简而言之:质疑编码模式不是编译器的工作.

你可以很好地编写一个Main方法,除了抛出一个什么都不做Exception.这是一个更容易检测的模式,也是一个更愚蠢的事情; 但编译器很乐意允许您的程序编译,运行,崩溃和刻录.

话虽如此,从技术上讲,就编译器而言,无限循环/递归是完全合法的,没有理由为什么它应该抱怨它.

实际上,在编译时很难弄清楚循环不能在运行时被破坏.可能会抛出异常,可能发生用户交互,状态可能会在特定线程的某个位置,正在监视的端口上发生变化等等......对于任何代码分析工具来说,存在太多可能性,毫无疑问,一个特定的递归代码段将不可避免地导致运行时溢出.

我认为防止这些情况的正确方法是通过单元测试组织.您在测试中覆盖的代码路径越多,您遇到这种情况的可能性就越小.