Я использую Visual Studio 2010 SP1 Ultimate в проекте библиотеки классов С# (.net 4), и мне интересно что-то...
Учитывая этот метод:
public void DoSomethingBrilliant(int input)
{
if (input == int.MaxValue)
throw new ArgumentOutOfRangeException("input");
input++;
Console.WriteLine(input);
}
Я получаю это предупреждение от анализа кода:
CA2233: Microsoft.Usage: исправьте потенциальное переполнение в операции "ввод + 1" в "Test.DoSomethingBrilliant(int)".
Я подумал про себя: это немного странно, так как я проверяю, что операция input++
не будет переполняться, выбросив это простое исключение в начале, но я изменил его на это:
public void DoSomethingBrilliant(int input)
{
if (input >= int.MaxValue)
throw new ArgumentOutOfRangeException("input");
input++;
Console.WriteLine(input);
}
и, конечно же, предупреждение ушло.
Теперь мой маленький мозг запутался, потому что, учитывая, что я получаю int в качестве аргумента, почему бы проверить, больше ли он чем максимальное значение, допустимое для целого числа, когда-либо предоставляющее какую-либо ценность?
Затем я вернулся к исходному биту кода и переключился на отладку, и он был построен без предупреждения! Любопытный и любопытный...
Я проверил различия между debug и release и обнаружил, что если я поставлю галочку в опции "Оптимизировать код", предупреждение из анализа кода появится справа.
Таким образом, оптимизация приводит к чему-то, что означает, что мне нужно проверить больше 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
Я вижу кучу дополнительных вызовов до операции броска, но я буду честен - я понятия не имею, что они делают!