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!

Giovanni Bassi

Arquiteto e desenvolvedor, agilista, escalador, provocador. É fundador e CSA da Lambda3. Programa porque gosta. Acredita que pessoas autogerenciadas funcionam melhor e por acreditar que heterarquia é mais eficiente que hierarquia. Foi reconhecido Microsoft MVP há mais de dez anos, dos mais de vinte que atua no mercado. Já palestrou sobre .NET, Rust, microsserviços, JavaScript, TypeScript, Ruby, Node.js, Frontend e Backend, Agile, etc, no Brasil, e no exterior. Liderou grupos de usuários em assuntos como arquitetura de software, Docker, e .NET.