No último fim de semana eu lancei um desafio de C# no Twitter. A pergunta era: nesse código, como é possível que a primeira soma não dê overflow, enquanto a segunda dá?

checked
{
    var y = n + 5;
    WriteLine(y);
    var x = m + 5;
    WriteLine(x);
}

Pense um pouco sobre a pergunta, abaixo vou colocar algumas opções de como resolver o enigma. Role a tela para ver a explicação.

.

.

.

.

.

.

.

.

.

O primeiro ponto a considerar é o contexto “checked”, que não é padrão no C#. Por padrão, após superar o valor máximo, o variável “dá a volta” e vai até o valor mínimo.

WriteLine("Is int.MaxValue + 1 == int.MinValue? " + (m + 1 == int.MinValue));
// output: Is int.MaxValue + 1 == int.MinValue? True

Se você colocar o código acima em um bloco “checked”, uma “OverflowException” será lançada.

Então, como resolver?

Alguns outros números funcionam um pouco diferente. Por padrão, os números de ponto flutuante (fload e double) não tem esse comportamento, então essa é uma opção válida:

var m = int.MaxValue;
var n = float.MaxValue; // ou double.MaxValue
checked
{
    var y = n + 5;
    WriteLine(y);
    var x = m + 5;
    WriteLine(x);
}

E “decimal”? Decimal é também ponto flutuante, mas funciona como os inteiros, também dá overflow.

Mas existe um inteiro que não dá overflow, e esse era o tipo que eu estava pensando quando fiz o desafio. É o IntPtr, que tem o tamanho nativo de um inteiro no sistema em que está rodando, e por isso é usado muito para ponteiros e HWDs. Eles também não geram a exception:

var m = int.MaxValue;
var n = IntPtr.MaxValue;
checked
{
    var y = n + 5;
    WriteLine(y);
    var x = m + 5;
    WriteLine(x);
}

Isso quer dizer que todo inteiro nativo é assim? E os novos tipos de inteiro nativos, “nint” e “nuint”?

Esses dois tipos foram criados para ficarem mais alinhados ao comportamento do sistema de inteiros do .NET, enquanto possuem o tamanho do sistema nativo (ou seja, assim como IntPtr, variam de acordo com a arquitetura, 32 ou 64 bits). Ou seja, causam uma exception caso tenham overflow num contexto “checked”.

Coloquei mais alguns exemplos em um Gist, caso queiram ver. É só rodar.

Você pode acessar o .NET Fiddle aqui, e fiz um Gist também, aqui.

Obrigado a todos se divertiram comigo no fim de semana, e se você tiver outras formas de resolver, comenta aqui no blog!