为什么需要检查大于Int32.MaxValue的值?

13
我正在使用Visual Studio 2010 SP1 Ultimate进行C#类库项目(.net 4)的开发,我对某些事情很好奇...
给定以下方法:
public void DoSomethingBrilliant(int input)
{
    if (input == int.MaxValue)
        throw new ArgumentOutOfRangeException("input");

    input++;

    Console.WriteLine(input);
}

我从代码分析中得到了如下的警告:

CA2233 : Microsoft.Usage : 请在“Test.DoSomethingBrilliant(int)”中修正操作“input+1”可能会溢出的问题。

我想了想,觉得这有点奇怪,因为我已经通过在开头抛出异常来检查input++操作是否会溢出,但我将其改成了这样:
public void DoSomethingBrilliant(int input)
{
    if (input >= int.MaxValue)
        throw new ArgumentOutOfRangeException("input");

    input++;

    Console.WriteLine(input);
}

然后警告果然消失了。

现在我的小脑袋很困惑,因为我得到的参数是一个int,那么为什么检查它是否大于整数允许的最大值会有任何价值呢?

然后我回到了原来的代码,并切换到了调试模式,没有警告就编译成功了!越来越奇怪了...

我检查了调试和发布之间的差异,发现如果我选中了优化代码选项,那么代码分析中的警告就会弹出。

所以优化会导致某些情况下需要检查是否大于int.MaxValue。嗯?为什么?我是不是太迟钝了?优化做了什么,使得我可能会得到一个比接受int的方法所允许的int.MaxValue还要大的int参数呢?

还是说这只是代码分析功能中的一个错误吗?
更新:
这是“未优化”版本的IL(在此版本中,代码分析得到了正确的结果):
.method public hidebysig instance void  DoSomethingBrilliant(int32 input) cil managed
{
  // Code size       40 (0x28)
  .maxstack  2
  .locals init ([0] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  ldc.i4     0x7fffffff
  IL_0007:  ceq
  IL_0009:  ldc.i4.0
  IL_000a:  ceq
  IL_000c:  stloc.0
  IL_000d:  ldloc.0
  IL_000e:  brtrue.s   IL_001b
  IL_0010:  ldstr      "input"
  IL_0015:  newobj     instance void [mscorlib]System.ArgumentOutOfRangeException::.ctor(string)
  IL_001a:  throw
  IL_001b:  ldarg.1
  IL_001c:  ldc.i4.1
  IL_001d:  add
  IL_001e:  starg.s    input
  IL_0020:  ldarg.1
  IL_0021:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0026:  nop
  IL_0027:  ret
} // end of method Test::DoSomethingBrilliant

这里是针对优化版本的翻译(它会出错):
.method public hidebysig instance void  DoSomethingBrilliant(int32 input) cil managed
{
  // Code size       31 (0x1f)
  .maxstack  8
  IL_0000:  ldarg.1
  IL_0001:  ldc.i4     0x7fffffff
  IL_0006:  bne.un.s   IL_0013
  IL_0008:  ldstr      "input"
  IL_000d:  newobj     instance void [mscorlib]System.ArgumentOutOfRangeException::.ctor(string)
  IL_0012:  throw
  IL_0013:  ldarg.1
  IL_0014:  ldc.i4.1
  IL_0015:  add
  IL_0016:  starg.s    input
  IL_0018:  ldarg.1
  IL_0019:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_001e:  ret
} // end of method Test::DoSomethingBrilliant

我看到在抛出操作之前有一堆额外的调用,但是说实话 - 我不知道它们是做什么的!
2个回答

7

或者,这只是代码分析功能中的一个错误吗?

看起来是的。说实话,并不奇怪-要使这种代码分析完美无缺非常棘手。鉴于任何特定的int不能大于int.MaxValue,因此>===肯定是等价的。


代码分析器可能认为不等运算符比简单的相等更重要。当我进行安全检查时,我会尽量去掉值域中的许多部分。 - bluevector
我很感兴趣为什么在非优化的“调试”汇编中它会得到正确的结果? - kmp
@kmp:不想说。我想知道 == 是否以某种奇怪的方式进行了优化。你有查看过 IL 吗? - Jon Skeet
是的,我确实查看了IL代码,但我并没有变得更明智(因为我并不真正理解IL代码)- 我只是为了完整性将其放在问题中。 - kmp
1
最有可能的是,代码分析正在查看执行流程而没有内联“int.MaxValue”,因此不知道要比较的值真正是“Int32”的最大值。 - Daniel Pryden

1
请看这些代码片段:
if (x == int.MaxValue) return;
// x != int.MaxValue

if (x >= int.MaxValue) return;
// x < int.MaxValue

并且

// x < int.MaxValue
// rewrite
// x + 1 <= int.MaxValue
x++;
// x <= int.MaxValue

x++ 的后置条件(通过推导)表明其前置条件必须为:

x < int.MaxValue 

只有在您检查以下情况的情况下才能建立:

x >= int.MaxValue

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