【问题标题】:Why would I need to check for greater than Int32.MaxValue?为什么我需要检查是否大于 Int32.MaxValue?
【发布时间】:2012-06-27 12:23:50
【问题描述】:

我在一个 c# 类库项目 (.net 4) 上使用 Visual Studio 2010 SP1 Ultimate,我对某事感到好奇...

鉴于此方法:

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.MaxValue 的 int 传递给接受 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

我在 throw 操作之前看到了一堆额外的调用,但老实说 - 我不知道他们做了什么!

【问题讨论】:

    标签: c# .net


    【解决方案1】:

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

    看起来像。老实说,这并不奇怪——让这种代码分析变得完美是非常棘手的。鉴于任何特定的int不能大于int.MaxValue>=== 绝对是等价的。

    【讨论】:

    • 代码分析器可能认为不等式运算符比简单的等式更重要。当我进行安全检查时,我当然会尽可能多地切断价值观。
    • 我很感兴趣为什么它会在未优化的“调试”程序集上正确运行?
    • @kmp:不想说。我想知道== 是否以某种奇怪的方式进行了优化。你看过 IL 吗?
    • 是的,我确实看过 IL,但我并没有真正更聪明(因为我并不真正了解 IL)——我只是为了完整性而将其留在问题中。
    • +1。最有可能的是,代码分析正在查看没有内联 int.MaxValue 的执行流程,因此它不知道所比较的值确实是 Int32 的最大值。
    【解决方案2】:

    查看这些代码片段:

    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
    

    【讨论】:

      猜你喜欢
      • 2011-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多