【问题标题】:pre/post-increment in conditional skipped by short-circuit evaluation短路评估跳过的条件前/后增量
【发布时间】:2013-06-18 20:49:29
【问题描述】:

所以,如果我有这样的表达:

if (obj != null && i++ % divisor == 0)
{
....
}

objnull,那么i 永远不会增加。如果我使用

i++; 
if (obj != null && i % divisor == 0)
{
....
}

当然,i 是递增的。

这是设计使然吗?我从优化的角度理解短路评估,但我(错误地)假设编译器会识别后增量表达式并无论如何都对其进行评估。

(如果这在规范中,我找不到它 - 只是在这里寻找一些(喘气)意见。)

更新

这是实际代码。

    private int _frameNumber = 0;

    private void simulator_OnFrameEnd(object sender, System.EventArgs e)
    {
        _frameNumber++; 

        if (_visualizer != null && _frameNumber % _config.VisualizerUpdateFrequency == 0)
        {
            var field = _simulator.GetField(_config.PreviewField);

            _visualizer.Update(field, _simulator.FrameTime);
        }

        if (_frameNumber % _config.OptimizerRegridFrequency == 0)
        {
            _simulator.UpdateFieldGrids();

        }

    }

【问题讨论】:

  • 如果你想要这种行为,你可以使用&
  • 如果你非要问这个问题,那就说明你的代码不可读。
  • 简单。其他人在阅读您的代码时也会有同样的问题。我建议你构建你的代码,这样就不会出现这个问题。
  • 当然行为是正确的。您误以为短路行为是一种优化。不是这样的。这是一个重要的保证。如果它是可选优化,那么您不能保证if (obj != null && obj.ToString().Length > 10) 不会崩溃!
  • @JohnSaunders 的建议是好建议;这段代码很难阅读,风格也很糟糕。如果您不了解 C# 中运算符的含义,那么 (1) 教育自己,以及 (2) 编写更容易让不了解其含义的人阅读和维护的代码。一个好的经验法则是每个表达式都应该有完全零副作用——表达式应该对它们的有用,而不是它们的效果—— - 并且每一个单独的语句都应该只引起一个副作用。具有多重副作用的陈述很难推理。

标签: c# compilation


【解决方案1】:

这绝对是设计使然。 MSDN 状态:

操作x && y对应操作x & y除了 如果 x 为假,则不评估 y,因为 AND 的结果 不管 y 的值是多少,操作都是假的。这被称为 “短路”评估。

所以,不管你的语句是什么,不管它是否有后增量,它是否短路都不会被评估。

【讨论】:

    【解决方案2】:

    后增量只是语句的后评估。由于该语句从未被评估(它是短路的)它被跳过。

    一种更直观的思考方式是将 if 语句想象为嵌套:

    if (obj != null)
    {
       if (i++ % divisor == 0)
       {
       ...
       }
    }
    

    在这里我们立即看到i 不会增加。同样,当 if 语句被短路时,行为与上述类似,并且后增量不会排队。

    【讨论】:

    • @DavidLively:正是因为修改状态后的表达式必须被执行!短路操作的重点是防止产生右手边的副作用
    • @EricLippert 我明白了,这当然是有道理的。然而,我那本被咖啡/啤酒弄脏的龙书副本让我对这个结构的目的和语义含义有了一个非常不同的解释。同样,我知道这是不好的风格 - 问题是关于生成代码的“正确性”。 (作为一名 EE,过去曾在编译器团队 (Keil) 工作过,我意识到这种级别的“错误”几乎从未发生在成熟的工具中。因此,我很困惑。)
    【解决方案3】:

    是的,这是设计使然。来自规范:

    7.12.1 布尔条件逻辑运算符

    操作 x && y 被评估为(bool)x ? (bool)y : false。在 换句话说,首先评估 x 并将其转换为类型 bool 。然后, 如果 x 为 true ,则评估 y 并将其转换为 bool 类型,并且此 成为运算的结果。否则,结果 操作是假的

    在您的代码中,y 是 i++ % divisor == 0,因此如果 obj 为空,则不会执行 i++

    【讨论】:

      【解决方案4】:

      frameNumber 不会增加,因为 _visualizer != null 返回 false。所以代码甚至不需要检查/执行增量和模运算。

      如果您切换检查(即if (_frameNumber++ % 5 == 0 && _visualizer != null)),那么_frameNumber 会增加...因为操作实际上已执行。

      所以是的:这是想要的行为,而且是设计使然。

      【讨论】:

        【解决方案5】:

        是的,这是设计使然。如果右侧评估为false,则永远不会评估左侧,包括可能发生的任何副作用(例如增量)

        操作x && y 对应于操作x & y,除了y 仅在xtrue 时才被评估

        ——来源MSDN

        是的,这是文档的旧版本,但语言更清晰,IMO。

        如果您想评估增量,请使用:

        if (_visualizer != null & _frameNumber++ % _config.VisualizerUpdateFrequency == 0)
        {
            ...
        }
        

        如果你想在visualizer == null而不是模数的情况下执行增量:

        var _prevFrameNumber = _frameNumber++;
        if (_visualizer != null && _prevFrameNumber % _config.VisualizerUpdateFrequency == 0)
        {
            ...
        }
        

        【讨论】:

          猜你喜欢
          • 2011-05-11
          • 2018-09-13
          • 2015-11-02
          • 1970-01-01
          • 2015-11-14
          • 2017-01-21
          • 2012-02-10
          • 2010-12-21
          • 1970-01-01
          相关资源
          最近更新 更多