C#, como muchos lenguajes de tipado estático, ofrece varios tipos que representan números enteros, cada uno con su propio rango de valores. En el extremo inferior, el tipo sbyte tiene un valor mínimo de -128 y un valor máximo de 127. Como todos los tipos enteros, estos valores están disponibles como <type>.MinValue y <type>.MaxValue. En el extremo superior, el tipo long tiene un valor mínimo de -9,223,372,036,854,775,808 y un valor máximo de 9,223,372,036,854,775,807. Entre ambos están los tipos short e int.
Cada uno de los tipos anteriores tiene su equivalente sin signo: sbyte/byte, short/ushort, int/uint y long/ulong. En todos los casos, el rango de valores va de 0 al máximo negativo con signo multiplicado por 2 más 1.
Los valores de los tipos enteros sin signo se representan con una sencilla representación en base 2. Los valores de los tipos con signo usan la representación de números con complemento a dos.
La multiplicidad de tipos enteros refleja las arquitecturas de las máquinas: el tamaño de los registros, el tamaño de los argumentos de las instrucciones de la CPU y el tratamiento del signo dentro de la CPU. Un valor de tipo long usa 64 bits, mientras que un valor de tipo sbyte usa 8 bits. En algunos casos habrá implicaciones en el rendimiento de la CPU, el uso de memoria e incluso el uso de disco (donde elegir un tipo entero más pequeño suele ser beneficioso). La selección del type entero también puede ser una manera aproximada de comunicar a otros desarrolladores información sobre el rango de valores esperado. El tipo int se usa ampliamente como el tipo predeterminado cuando no se ha identificado nada especial sobre el uso particular. Los tipos long o ulong se usan ampliamente como un identificador simple. El tamaño del tipo en bytes determina el rango de valores.
Los tipos que hemos visto hasta ahora son tipos primitivos. Cada uno tiene un alias de struct que implementa campos (como MinValue) y métodos (como ToString()) asociados al tipo.
| Tipo | Estructura | Ancho | Mínimo | Máximo |
|---|---|---|---|---|
sbyte |
SByte |
8 bit | -128 | +127 |
short |
Int16 |
16 bit | -32_768 | +32_767 |
int |
Int32 |
32 bit | -2_147_483_648 | +2_147_483_647 |
long |
Int64 |
64 bit | -9_223_372_036_854_775_808 | +9_223_372_036_854_775_807 |
byte |
Byte |
8 bit | 0 | +255 |
ushort |
UInt16 |
16 bit | 0 | +65_535 |
uint |
UInt32 |
32 bit | 0 | +4_294_967_295 |
ulong |
UInt64 |
64 bit | 0 | +18_446_744_073_709_551_615 |
Una variable (o expresión) de un tipo se puede convertir fácilmente a otro. Por ejemplo, en una operación de asignación, si el tipo del valor que se asigna (rhs) asegura que el valor cabrá dentro del rango del tipo al que se asigna (lhs), entonces basta con una asignación simple:
ulong ul;
uint ui = uint.MaxValue;
ul = ui; // no problem
Por otro lado, si el rango del tipo desde el que se asigna no es un subconjunto del rango de valores del destinatario, entonces se requiere una operación de conversión, (), incluso si el valor concreto está dentro del rango del destinatario:
uint ui;
short s = 42;
ui = (uint)s;
En el ejemplo anterior, si el valor estuviera, en cambio, fuera del rango del destinatario, se produciría un desbordamiento. Consulta overflow.
La necesidad de convertir viene determinada por los dos tipos involucrados, no por un valor concreto.
Los siguientes párrafos tratan la conversión de los tipos enteros. Consulta casting, que ofrece un análisis más amplio de la conversión y la conversión de tipos. Consulta esa documentación para ver un análisis de la conversión entre tipos enteros y números de punto flotante, char y bool.
El sistema de tipos de C# es algo más estricto que el de C o el de Javascript y, como consecuencia, las operaciones de conversión están más restringidas. La conversión implícita tiene lugar entre dos tipos numéricos siempre que el tipo «de destino» pueda preservar la escala y el signo del valor del tipo «de origen».
Una conversión implícita no se indica con ninguna sintaxis especial.
Cuando los tipos numéricos no se pueden convertir de forma implícita, por lo general puedes usar el operador de conversión explícita.
Cuando el valor que se convierte no puede ser representado por el tipo «de destino» porque no es lo bastante amplio o hay un conflicto de signos, se puede lanzar una excepción de desbordamiento.
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)
La clase BitConverter ofrece una forma cómoda de convertir tipos enteros desde y hacia arrays de bytes.