如何使用编译器优化使代码更具可读性?

时间:2012-03-22 16:23:55

标签: c# readability

更频繁地阅读代码然后更新。编写更易读的代码比编写强大且令人讨厌的代码更好,因为编译器可以优化以实现最佳执行。

例如,请参阅下面的代码 - 可以通过组合嵌套的if语句来压缩此代码,但是在我们保持其可读性的同时,编译器是否会优化此代码以实现最佳执行?

// yeild sunRays when sky is blue.
// yeild sunRays when sky is not blue and sun is not present.
if (yieldWhenSkyIsBlue)
{
    // if sky is blue and sun is present -> yeild sunRaysObjB.
    if (sunObjA != null)
    {
        yield return sunRaysObjB;
    }
    else
    { 
       // do not yield ; 
    }    
}
else
{
    // if sky is not blue and sun is not present -> yeild sunRaysObjB.
    if (sunObjA == null)
    {
        yield return sunRaysObjB;
    }
}

与此类似:

// yeild sunRays when (sky is blue) or (sun is not present and sky is blue).
// (this interpretation is a bit misleading as compared to first one?)
if(( sunObjA == null && yieldWhenSkyIsBlue ==false) || (yieldWhenSkyIsBlue && sunObjA != null) )
{
    yield return sunRaysObjB;
}

阅读第一个版本会更好地描述用户未来的增强功能\更新?代码的第二个版本更短但是阅读它并不会使用例非常明显或者是这样吗?除了简洁的代码之外,第二种情况还有其他优点吗?

更新#1 :是的,它会在两种情况下都返回ObjB,但根据条件,它可能根本不会产生。所以策略决定何时屈服,何时不屈服。 (可读性为imp的另一个原因)

更新#2 :更新到网站更好的示例。从stripplingWarrior

复制语法

更新#3 :更新为“当太阳出来且天空是蓝色的时候你会发生什么?”

5 个答案:

答案 0 :(得分:7)

我认为第二个代码示例更具可读性,并且无论如何都具有非常优化的优势。

大多数程序员会发现这个逻辑流程显而易见且自然:如果ObjA为null,或者如果它不为null且设置了howtoYieldFalg,则返回ObjB。

但是如果我必须选择使这样的代码更具可读性并使其最佳化,那么我首先要让它可读。只有当我发现它是瓶颈的源头时,我才会费心去优化它。在这种特殊情况下,我几乎可以保证使用yield return会比对条件的次优评估引入更多的开销。

更新

再看看你的代码示例:它们在逻辑上并不相同。当太阳出来天空是蓝色时,你期望发生什么?在这种情况下,第二个代码示例正确地允许太阳光线照射,而第一个示例则没有。

事实上,在第一个案例中引入一个很多人未能抓住这么长时间的错误的事实应该足以证明第二种方法更好。即使是有经验的程序员,所有那些嵌套的if/else语句也很难保持直接。简单的布尔逻辑更容易保持直线,特别是一旦使用赋予其含义的变量名称。

更新2

基于进一步的解释和一点点创造力,我将建议一种使用注释和变量名称来提高清晰度的方法:

    /* Explanation: We live on a strange planet where the sun's
     * rays can shine if the sky is blue while the sun is out,
     * or if the sky is not blue and there is no sun. */
    bool sunIsPresent = sunObjA != null;
    if ((skyIsBlue && sunIsPresent) ||
        (!skyIsBlue && !sunIsPresent))
    {
        yield return sunRaysObjB;
    }

答案 1 :(得分:3)

编译器通过您组织程序控制流程的任何方式进行优化,因此您真的不必担心它。

编译器的弱点是,它们只是基于保留代码语义进行优化,而不是保留您想要的含义。我在LLVM中编译了你的例子,这里是生成的控制流图:

first example

second example

我很惊讶地发现两个CFG略有不同。您将注意到,第一个是较小的指令,但在第二个图中,存在一个只通过一次比较的退出节点的路径,而在第一个中,总是需要进行两次比较。

事实上,对可能的路线的进一步追踪产生了第一个例子可能具有6,8,8,6个指令长度的路径,而第二个示例具有分别为8,10,10的路径。在两种情况下,平均运行长度为7条指令,但我们可以看到第一种情况具有更好的最佳运行长度。没有更多信息,编译器无法判断哪个更好。

tldr:编译器会做出神奇的事情,不用担心,编码你认为最好的方式。

答案 2 :(得分:2)

这可能不是流行的观点,但我绝对不依赖编译器来执行此类型的优化。 (它可能这样做,我不知道。)我不认为第二个例子是令人讨厌的 - 对我而言,它更清楚地描述了这两个条件是相互关联的。

通常我会尝试编写尽可能优化的代码而不会让它变得非常神秘,然后让编译器对其进行优化。

答案 3 :(得分:2)

虽然我没有测试过这个特殊情况,但我愿意打赌生成的代码之间没有显着差异,如果有的话。

答案 4 :(得分:1)

除非你是为了娱乐或专业用例而做的,否则我认为人类可读性是迄今为止优质代码的重要品质。编译器会将你的大部分表达代码折叠成更有效的形式,以及你可能不会注意到的错过的东西。

鉴于此,惯用代码即使不那么简洁也更容易阅读。经验丰富的语言读者会比不熟悉的代码更快地识别出一种常见的模式,可以说是“更人性化”但却打破了熟悉的模式。循环/递增构造是代码的一个很好的例子,应该不足为奇。所以,我的方法是:表达,但不要太聪明。