C#, como muitas linguagens de tipagem estática, oferece vários tipos que representam números inteiros, cada um com seu próprio intervalo de valores. Na extremidade inferior, o tipo sbyte tem valor mínimo de -128 e valor máximo de 127. Como em todos os tipos inteiros, esses valores estão disponíveis como <type>.MinValue e <type>.MaxValue. Na extremidade superior, o tipo long tem valor mínimo de -9.223.372.036.854.775.808 e valor máximo de 9.223.372.036.854.775.807. Entre eles ficam os tipos short e int.
Cada um dos tipos acima tem um equivalente sem sinal: sbyte/byte, short/ushort, int/uint e long/ulong. Em todos os casos, o intervalo de valores vai de 0 até o máximo negativo com sinal vezes 2 mais 1.
Os valores dos tipos integrais sem sinal são representados com uma simples representação em base 2. Os valores dos tipos com sinal usam a representação de números com sinal complemento de dois.
A multiplicidade de tipos inteiros reflete as arquiteturas de máquina, no tamanho dos registradores, no tamanho dos argumentos das instruções da CPU e no tratamento do sinal dentro da CPU. Um valor do tipo long usa 64 bits, enquanto um valor do tipo sbyte usa 8 bits. Em alguns casos, isso terá implicações no desempenho da CPU, no uso de memória e até no uso de disco (onde a escolha de um tipo inteiro menor geralmente é vantajosa). A escolha do type inteiro também pode ser uma forma aproximada de comunicar a outros desenvolvedores o intervalo de valores esperado. O tipo int é amplamente usado como o tipo padrão quando nada de especial foi identificado sobre o uso específico. O long ou o ulong é amplamente usado como um identificador simples. O tamanho do tipo em bytes determina o intervalo de valores.
Os tipos discutidos até aqui são tipos primitivos. Cada um é pareado com um alias struct que implementa campos (como MinValue) e métodos (como ToString()) associados ao tipo.
| Tipo | Struct | Largura | Mínimo | Máximo |
|---|---|---|---|---|
sbyte |
SByte |
8 bits | -128 | +127 |
short |
Int16 |
16 bits | -32_768 | +32_767 |
int |
Int32 |
32 bits | -2_147_483_648 | +2_147_483_647 |
long |
Int64 |
64 bits | -9_223_372_036_854_775_808 | +9_223_372_036_854_775_807 |
byte |
Byte |
8 bits | 0 | +255 |
ushort |
UInt16 |
16 bits | 0 | +65_535 |
uint |
UInt32 |
32 bits | 0 | +4_294_967_295 |
ulong |
UInt64 |
64 bits | 0 | +18_446_744_073_709_551_615 |
Uma variável (ou expressão) de um tipo pode ser convertida facilmente em outro. Por exemplo, em uma operação de atribuição, se o tipo do valor que está sendo atribuído (lado direito) garantir que o valor caberá no intervalo do tipo de destino (lado esquerdo), então há uma atribuição simples:
ulong ul;
uint ui = uint.MaxValue;
ul = ui; // no problem
Por outro lado, se o intervalo do tipo de origem não for um subconjunto do intervalo de valores do tipo de destino, será necessária uma operação de conversão () mesmo que o valor em questão esteja dentro do intervalo do destino:
uint ui;
short s = 42;
ui = (uint)s;
No exemplo acima, se o valor estivesse fora do intervalo do destino, ocorreria um overflow. Veja overflow.
A necessidade de conversão é determinada pelos dois tipos envolvidos, e não por um valor em particular.
Os parágrafos a seguir discutem a conversão de tipos integrais. (veja casting) oferece uma discussão mais ampla sobre conversão de tipos, tanto implícita quanto explícita. Consulte essa documentação para uma discussão sobre a conversão entre tipos integrais e números de ponto flutuante, char e bool.
O sistema de tipos do C# é um pouco mais rígido que o do C ou o do JavaScript e, por isso, as operações de conversão são mais restritas. A conversão implícita acontece entre dois tipos numéricos desde que o tipo de destino consiga preservar a escala e o sinal do valor do tipo de origem.
Uma conversão implícita não é indicada por nenhuma sintaxe especial.
Quando os tipos numéricos não podem ser convertidos implicitamente, você geralmente pode usar o operador de conversão explícita.
Quando o valor sendo convertido não pode ser representado pelo tipo de destino porque ele não é largo o suficiente ou há um conflito de sinal, uma exceção de overflow pode ser lançada.
int largeInt = Int32.MaxValue;
int largeNegInt = Int32.MinValue;
ushort shortUnsignedInt = ushort.MaxValue;
// implicit cast
int from_ushort = shortUnsignedInt; // 65535
float from_int = largeInt; // -21474836E+09
// explicit cast
uint from_largeInt = (uint)largeInt; // 2147483647
uint from_neg = (uint) largeNegInt; // 2147483648 or OverflowException is thrown (if checked)
A classe BitConverter oferece uma forma prática de converter tipos inteiros de e para arrays de bytes.