清理病态嵌套的 "if { } else { if { } else { if { ... } } }" 语句。

10

我目前不幸地在处理别人的C#代码,这个代码真是让我吃惊。我不知道之前的那个人是如何维护这份代码的,因为它的各种病态已经导致了IDE、编译器和运行时环境的崩溃...

我今天面临的问题涉及一个15兆字节的源文件,其中有一些令人难以置信的病态嵌套代码,例如:

if(var == 0) {
  // do stuff
}
else {
  if(var == 1) {
    // do stuff
  }
  else {
    if(var == 2) {
      // do stuff, identical word for word to the `var == 1` case
    }
    else {
      // etc.
    }
  }
}

这种写法本来就是一个值得质疑的样式选择。然而,这与代码的另一个问题相结合:其中一些块的深度接近1000层(我测量过最深的块超过了700层)。我真诚地希望在我之前,这位程序员被迫强制离开这个代码之前,已经运行了一个样式工具,以避免我目前看到的可怕情况。我无法想象他们现在可能会编写这段代码,特别是由于每第三或第四次对代码进行编辑都会导致IDE崩溃(有时还会删除源文件的副本)。

我编写了一个基于正则表达式的简单工具来尝试压缩较简单的情况,但它似乎只能处理一半,然后破坏了这个特定的代码 (我不确定它失败是因为这个代码有时也使用预处理器条件语句,还是因为最长的匹配项将近10MB并且Lua的正则表达式匹配器无法应对)。我希望有一个广泛使用的工具或技术可以解决这个问题。我已经不得不使用astyle清理代码中的其他一些样式“问题”。 astyle的--remove-brackets选项几乎可以做到我想要的效果,但需要花括号里的语句是单行上的单个语句,而这在这里绝对不是这种情况...(只是为了打好我的“i”的点,我检查过了; astyle没有创建这个特定的问题。)

编辑:深入研究这个问题代码,发现像这样的东西:

#if OneThing
int num2296 = otherThing();
#endif
#if AnotherThing
int num44 = otherThing()
int num45 = 0;
#endif
int num72 = 0;
#if OneThing
int num45 = 0; // note: multiple equivalent declarations of num45
#endif
#if OneThing
for(int num2297 = 0; num2297 < num2296; ++num2297) {
  num45 = doSomething(num2297);
#endif
#if AnotherThing
for(int num43 = 0; num43 < num44; ++num43) {
  num45 = doSomething(num43);
#endif
  if(somethingElse(num45)) {
    ++num72;
  }
} // note: only one closing brace for the two protected by #ifs

这段代码有两个版本,用于不同的目的。其中一个版本定义了OneThing,另一个版本定义了AnotherThing。然而,这两个版本的大部分区别只在于变量名不同,逻辑相同(并非全部相同)。

以上片段末尾的大括号错误说明了我的简单工具为什么会出错。这看起来越来越像是故意设计的保障措施,而不是无辜的无能。如果代码曾经处于使用反编译器生成类似于num2276这样的变量名的点上,那么它现在已经不再是那个点了。

不幸的是,这意味着自动化工具可能不能独立完成所有任务。我必须慢慢地修复上一个程序员造成的损害。我把这个问题留在这里,以便万一有一个神奇的工具,可以将两个版本都转换为SSA并识别和合并它们的逻辑等价性,然后再转换回去......


switch case可以是一种解决方案,或者使用责任链模式。 - Sir Rufo
1
似乎在示例代码中,使用switch语句会更好。顺便说一句,好问题。 - Maciej Los
1
我可能会通过将if块提取到方法中,不断深入地进行重构。这个视频给出了我所说的意思实用重构(不是我的视频)。我已经链接到了一个相关的时间戳,但是观看整个视频非常值得。它演示了如何重构一些相当可怕的代码,但你的代码可能会更好。 - Tone
1
当我看到这样的代码时,它让人感觉像是自动生成的。 - Eris
我愿意签署保密协议来查看你所拥有的代码。在我们的代码库中,最糟糕的情况是有很多文件长达8.5千行。 - Tanveer Badar
显示剩余6条评论
1个回答

6
您可以使用Roslyn来重写代码。将源代码作为文本进行修改并不是一个好的方法。使用Roslyn,您可以将其作为语法树进行修改。
也许这可以帮助您将所有内容平铺?
if (a)
 if (b) F2()
 else F3();
else
 F4();

Could become:

if (a && b) F2();
else if (a && !b) F3();
else F4();

那样源代码就成为了一个平坦的列表,更容易看出分支在何种情况下被执行。

Roslyn看起来非常适合我想做的事情。由于我还不太熟悉C#,所以我会先尝试一些其他方法,但是知道这种工具存在可以作为备选方案非常好。 (如果没有别的,当我处理最后一个源文件时,它肯定会非常有用...该文件超过25MB,其中至少有15MB是制表符。O_O) - Solra Bizna
也许那个人使用了Resharper的“内联”功能来内联许多方法,作为一种报复方式。这样做会导致代码大小呈指数级增长。 - usr
我越看这段代码,整个东西就越恶意。我编辑了原始帖子,展示了另一层疯狂,这似乎会挫败使用Roslyn自动撤消损坏的目的。 - Solra Bizna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接