C# 编译器魔法:为什么?

C# 编译器魔法:为什么?,c#,optimization,compiler-construction,C#,Optimization,Compiler Construction,我刚刚注意到,给定以下代码: if (x.ID > 0 && !x.IsCool) Microsoft C#3.0(VS2008 SP1)编译器将对其进行以下优化: if (!((x.Id <= 0) || x. IsCool)) if(!((x.Id从语言语义学的角度来看,我认为这两个表达式是完全等价的。两种方法都涉及短路 安德鲁的回答已经获得了10张赞成票,这让我有些震惊;我觉得这听起来像是胡说八道,但也许我真的错过了一些微妙的东西 编辑 总而言之: OP的问

我刚刚注意到,给定以下代码:

if (x.ID > 0 && !x.IsCool)
Microsoft C#3.0(VS2008 SP1)编译器将对其进行以下优化:

if (!((x.Id <= 0) || x. IsCool))

if(!((x.Id从语言语义学的角度来看,我认为这两个表达式是完全等价的。两种方法都涉及短路

安德鲁的回答已经获得了10张赞成票,这让我有些震惊;我觉得这听起来像是胡说八道,但也许我真的错过了一些微妙的东西

编辑

总而言之:

OP的问题是“为什么会发生这种优化”

事实上,没有“优化”发生。两个C#源代码在逻辑上是等价的。“.Net Reflector”或任何其他反汇编工具可能同样可能将同一个IL反编译为一个或另一个。在IL级别,只有一系列条件跳转,因此不一定有办法知道“哪条路是if,哪条路是else”或其他类似的DeMorgan等价物

令人着迷的是,人们非常乐意对这个问题的答案投赞成票或反对票,即使(或者可能是因为)原来的问题没有多大意义(或者依赖于错误的假设)

令人高兴的是,最终群众的智慧(以及像@Mehrdad这样的聪明人)占了上风。祝StackOverflow万岁

(我把我的答案做成了一个维基,因为我不想让rep因为“讲述一个问题”而被授予“一个问题的好答案”。但我认为这个问题的故事很有趣。)C#编译器肯定不会为你的代码片段生成等价的C#代码。它被编译成IL。基本上,你看到的(我猜是从Reflector看到的)是反编译器为该IL吐出的等价的C#代码

  • 语言规范没有说明什么是“未优化的”“代码是。C#编译器可以生成任何有效的、功能等效的代码。即使没有打开优化开关,编译器也可能进行基本优化。除此之外,你不能说什么是编译器的自然,也不能说编译器是否有意对其进行优化

  • if
    语句作为一个整体,根据“and”子句中指定的每个表达式的值,作为一系列条件分支进行求值。表达式不会在单个代码块中使用“and”进行求值指令。反编译器的输出是从这些分支推断出来的。反编译器不能总是推断出您编写的原始表达式。它只输出等价的东西

  • 同样,此代码段之间的区别:

    if (a) { something(); }
    else { somethingElse(); }
    
    if (!a) { somethingElse(); }
    else { something(); }
    
    这段话:

    if (a) { something(); }
    else { somethingElse(); }
    
    if (!a) { somethingElse(); }
    else { something(); }
    

    通过查看编译后的代码,您无法区分它。

    if编译成一个条件跳转操作码。从表达式中取消否定可以通过将条件跳转目标与跳转块交换来优化它。

    您是如何发现的?@Konrad,您能引用规范吗?我不喜欢强加“pr负担”对你不客气,但我觉得这很可疑。好吧,康拉德,我99%肯定你错了,但我现在也没有要引用的规范。编译器不允许修改你程序的语义。另一方面,调试器可以免费使用。调试器这样做并不是因为它“合法”(毕竟,您可以在调试器中的任何变量中插入新值),但因为它是有用的/方便的。被调试的程序不希望表现出与没有调试器的程序相同的行为。但是编译器没有这种快慢的奢侈。@pst:没关系;当且仅当x.ID>0时才会调用getter。@Konrad:但调试器并没有创建不同的行为,您的int使用调试器的eraction创建了不同的行为。可能只是语义上的问题,但我认为,一旦您作为用户开始在调试器中触摸东西,就不能保证您的程序会有相同的行为。(经典示例:在存在断点的情况下,度量时间的代码应该如何表现?)+1这是正确的答案,并对boot进行了简洁的解释。:)我不想知道这是否是一个“优化”(因此引用)。我想知道的是| |值的组合。我原以为IL会使用实数布尔或/和运算,但事实并非如此。在引擎盖下,只使用了ble.s、br.s和brtrue.s的组合。@Brian-考虑到我100%不正确的事实,我也大吃一惊:)这就是我不经思考就即兴发帖所得到的。你完全正确,一个案例区分显示两个代码生成完全相同的分支,因此产生了相同的短路。然而,这很微妙,乍一看,我实际上认为安德鲁是对的(然后我在头脑中做了所有案例):)我很高兴StackOverflow(编辑、wiki风格、投票)的设计意味着最终一切都会顺利进行,即使在问题的最初几分钟内可能会像坐过山车一样