问题 如果语句似乎正在评估,即使条件评估为false


昨晚工作到很晚,我们试图弄清楚为什么会出现问题。如果不应该进行验证检查失败。

我们最终在这段代码中添加了一个print语句(从Reflector中反汇编,以便检查代码实际上是我们编写的代码):

public static string Redacted(string name, DateTime lastModified)
{
    long ticks = lastModified.Ticks;
    if ((ticks != (ticks - (ticks % 10000L))) &&
            (lastModified != DateTime.MaxValue))
    {
        Log.Debug(string.Format("Last Modified Date = '{0}'. Ticks = '{1}'. TicksCalc = '{2}'",
            lastModified.ToString("dd/MM/yyyy hh:mm:ss.fff"),
            ticks, ticks - (ticks % 10000L)));

它打印(重新格式化):

Last Modified Date = '22/03/2011 12:16:22.000'.
Ticks     = '634363497820000000'.
TicksCalc = '634363497820000000'            

但条件是“ticks“(等于上面印刷的Ticks)不等于”(ticks - (ticks % 10000))“(等于TicksCalc)!634363497820000000!= 634363497820000000?!

为了确定这里发生了什么,我们又添加了两个语句:

long ticks = lastModified.Ticks;
/* Added following two lines: */
long num2 = ticks - (ticks % 10000L);
Log.Debug((ticks == num2).ToString());
/* */
if ((ticks != (ticks - (ticks % 10000L))) &&
        (lastModified != DateTime.MaxValue))
{
    Log.Debug(string.Format("Last Modified Date = '{0}'. Ticks = '{1}'. TicksCalc = '{2}'",
        lastModified.ToString("dd/MM/yyyy hh:mm:ss.fff"),
        ticks, ticks - (ticks % 10000L)));

应该有,这个印刷 true (当用相同的值测试时),并没有写第二行。

感觉有点迷失,我们再次删除了两行,重新编译,并重新。原来的行为重演了。

今天早上, 我录制了一段视频

首先,视频显示使用“损坏”代码在方法中命中断点,然后使用“工作”代码重建和重新运行。请注意,即使调试器显示了 if 条件评估 false,身体仍然进入。

我已经看到这样的事情发生在调试器观察之前,因为调试器强制要求评估一些事情,但无论是否使用调试器都会发生这种情况。

此外,这仅在发布模式下发生(即启用了JIT优化)。

以下是两个版本的反汇编方法: 加工不工作。我不能真正阅读汇编,所以我将它们发布在这里是为了澄清。

我希望答案不是我完全忽略的明显的答案......!

编辑:这是IL。我认为它没有任何问题,因为它反编译为正确的C#:

更新

被Microsoft确认为bug,将在下一个版本中修复


9856
2018-05-19 07:04


起源

好视频!很长一段时间没见过这种行为。我曾经有同样的问题(但在Java中)。添加单个“System.out.println();”我也解决了这个问题。这真奇怪...... - Christian
@Porges我们可以得到MSIL(.NET程序集,而不是x86程序集:))?您可以使用.NET Reflector获取所需的特定方法。 - pickypg
@pickypg:我添加了它。我不认为它有任何问题,因为它反编译为正确的C#代码(虽然我没有通过手工完成)。 - porges
@ohmantics:你能解释一下你为什么要删除它 assembly 标签?我已经链接了这个问题中包含的x86汇编代码...... - porges
我已经用MS连接提交了一个bug报告: connect.microsoft.com/VisualStudio/feedback/details/671105/... - CodesInChaos


答案:


我用简化的代码进行了一些实验: http://nopaste.info/2c99a0e028_nl.html

最有趣的变化是:

static readonly long variableZero=0; 
const long constZero=0; 

public static void Broken2( long ticks2) 
 { 
     long ticks = ticks2+variableZero; 
     if (ticks != (ticks - (ticks % 10000L))) 
     { 
         string.Format("Last Modified Date = '{0}'. Ticks = '{1}'. TicksCalc = '{2}'", 
             "n/A", 
             ticks, ticks - (ticks % 10000L)).Dump(); 
     } 
 }

如果我更换 variableZero 同 constantZero 有用。


所以我很确定它是一个抖动或编译器错误。

我在MS Connect上提交了一个bug报告: https://connect.microsoft.com/VisualStudio/feedback/details/671105/jitter-or-c-compiler-bug#details


更新:如果没有附加调试器,则只会发生奇怪的行为。即,当启用Jit优化时。所以我很确定这是一个抖动错误。

对于没有linq-pad的人来说,现在有一个普通的C#控制台项目: http://nopaste.info/00a0e37328_nl.html


9
2018-05-27 10:55



哇谢谢!我不认为可以在我们的库的上下文之外重现它:) - porges
RE:更新 - 我以为我在原帖中提到过这个,但我似乎已将其编辑出来了。我会加一点说明。 - porges
他们(最终)回复了这个错误并声明它将在下一个框架版本中修复。我再次检查过,它似乎没有出现在最近的KB2468871 / KB2533523版本中,所以希望它会成为下一个版本。将此标记为答案。 - porges


看看这个帖子。

如果在Visual Studio 2008中声明怪异

归结到这一点,你无法一直信任调试器。

要“修复”if语句,请添加一个空 别的{} 声明。调试器将按预期工作。


1
2018-05-19 07:12



无论是否附加调试器,都会发生此行为(第一个输出来自未附加调试器的运行)。我只是能够在那里打破并确保。 - porges
我实际上直到今天早上都没有附加调试器,因此第一个输出,“修复”和“修复”被删除后的再现都没有调试器干扰。 - porges


这确实看起来像是 - 哼哼 - jitterbug。你可以在“if”语句中设置一个断点,并在它击中之后向我们展示反汇编视图的截图吗?


1
2018-05-25 22:42



x86反汇编已经链接了吗? - porges
所以它是 - 抱歉。不幸的是,我现在没有时间对其进行分析,但如果没有其他人,我明天再看看。 - 500 - Internal Server Error


我刚才有类似的东西。 在我的情况下,它与我正在比较2个整数值的事实有关,其中一个值实际上是对盒装整数的引用而另一个值是真正的原始值。

问题是如果你打印出盒装整数和基元的值,它们看起来是一样的,但比较它们是另一回事。您将获得参考比较而不是值比较。

答案很简单:

long ticks = lastModified.Ticks;
long num2 = ticks - (ticks % 10000L);
if ((ticks != num2) && (lastModified != DateTime.MaxValue))
{ do your thing here! }

1
2018-05-26 15:47



我看到这里没有拳击。 - CodesInChaos


太疯狂了。您是否尝试过重新排序if语句?

if (lastModified != DateTime.MaxValue && ticks != (ticks - (ticks % 10000L))

此外,如果这不起作用(因为它不应该,首先考虑它不应该是一个问题),你能否以有问题的形式显示代码的实际IL?

还有一件事,不可能 ticks 检查简化为:

(ticks % 10000L) != 0

0
2018-05-20 03:33