Menu

Tipi di dati in C#: int, double, decimal, char, bool e i loro intervalli

Tutti i tipi predefiniti di C# con dimensione, intervallo e suffisso dei letterali, la differenza tra tipi valore e tipi riferimento, perché decimal è il tipo giusto per il denaro, l'overflow degli interi con checked e i valori predefiniti.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

C# è a tipizzazione statica: ogni variabile, campo ed espressione ha un tipo noto in fase di compilazione. Il linguaggio ha un insieme di tipi predefiniti con parole chiave proprie (int, double, string...). Ogni parola chiave è un alias di un tipo del namespace System, quindi int e System.Int32 sono lo stesso tipo, così come string e System.String.

I tipi predefiniti

TipoTipo .NETDimensioneIntervallo o precisioneLetterale
boolBoolean1 bytetrue o falsetrue
byteByte1 byteda 0 a 255200
sbyteSByte1 byteda -128 a 127-5
shortInt162 byteda -32,768 a 32,7671200
ushortUInt162 byteda 0 a 65,53560000
intInt324 byteda -2,147,483,648 a 2,147,483,64742
uintUInt324 byteda 0 a 4,294,967,29542u
longInt648 bytecirca ±9.2 × 10^1842L
ulongUInt648 byteda 0 a circa 1.8 × 10^1942ul
floatSingle4 bytecirca ±3.4 × 10^38, ~6-9 cifre2.5f
doubleDouble8 bytecirca ±1.8 × 10^308, ~15-17 cifre2.5 o 2.5d
decimalDecimal16 bytecirca ±7.9 × 10^28, 28-29 cifre2.5m
charChar2 byteuna unità di codice UTF-16, da U+0000 a U+FFFF'A'
stringStringriferimentotesto di qualsiasi lunghezza, immutabile"hello"
objectObjectriferimentola base di ogni tiponew object()

Un letterale intero senza suffisso è un int, oppure il primo tra uint, long e ulong in grado di contenerlo se è troppo grande per un int, e un letterale con il punto decimale è un double. Ecco perché float f = 2.5; e decimal d = 2.5; non compilano: il letterale è un double, e non esiste una conversione implicita da double a float o decimal. Aggiungi il suffisso: 2.5f, 2.5m.

Ogni tipo numerico espone i propri limiti come costanti:

Output:

byte:  0 to 255
short: -32768 to 32767
int:   -2147483648 to 2147483647
long:  -9223372036854775808 to 9223372036854775807
decimal max: 79228162514264337593543950335
double max:  1.798E+308
sizes: int 4, long 8, double 8, char 2
255 161 3000000000

In pratica la maggior parte del codice ne usa quattro: int per conteggi e indici, long per ID e totali grandi, double per misure e calcoli matematici, e decimal per il denaro.

decimal vs double

double (e float) memorizzano i numeri in binario. La maggior parte delle frazioni decimali, 0.1 compreso, non ha una forma binaria esatta, quindi viene memorizzata come il valore rappresentabile più vicino e piccoli errori si accumulano. decimal memorizza un intero in base 10 più una scala, quindi 0.1 è esattamente 0.1.

Output:

False
True
0.30000000000000004
0.3
double sum == 1.0: False
decimal sum == 1.0: True
Total: 59.97
Split three ways: 19.99

Usa decimal per denaro, prezzi, tasse e qualsiasi cosa una persona sommerà a mano e confronterà. Conserva gli zeri finali dei suoi input (1.50m viene stampato come 1.50) e ha da 28 a 29 cifre significative.

Usa double per misure fisiche, statistica, geometria e giochi. È varie volte più veloce, usa metà della memoria e il suo intervallo è enorme. Non confrontare mai due double calcolati con ==; confronta la differenza con una tolleranza: Math.Abs(x - y) < 1e-9.

decimal non fa miracoli: 1m / 3m resta comunque 0.3333333333333333333333333333, e l'aritmetica con double non lancia eccezioni nella divisione per zero (produce infinito, o NaN per 0.0 / 0.0), mentre la divisione per zero con decimal e con gli interi lancia una DivideByZeroException.

Overflow degli interi e checked

L'aritmetica intera che supera l'intervallo del tipo per impostazione predefinita ricomincia dall'altro estremo, in silenzio. Il compilatore rileva l'overflow nelle espressioni costanti, ma non quando i valori arrivano da variabili:

Output:

-2147483648
OverflowException
-1294967296
2147483648

checked(...) o un blocco checked { ... } trasforma l'overflow in una OverflowException. Puoi anche attivarlo per l'intero progetto con <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>. Scrivere int x = int.MaxValue + 1; con delle costanti è un errore di compilazione (CS0220), perché il compilatore vede l'overflow.

L'ultima riga mostra la soluzione per la maggior parte dei bug di overflow: convertire in un tipo più ampio prima dell'operazione. (long)(max + 1) sarebbe sbagliato, perché quando il cast viene eseguito l'addizione è già andata in overflow.

Tipi valore e tipi riferimento

Ogni tipo C# è un tipo valore o un tipo riferimento, e la differenza decide cosa fa l'assegnazione.

  • Tipi valore: tutti i tipi numerici, bool, char, DateTime, gli enum e ogni struct. La variabile contiene il dato; l'assegnazione lo copia.
  • Tipi riferimento: string, gli array, object e ogni class. La variabile contiene un riferimento a un oggetto; l'assegnazione copia il riferimento, quindi entrambe le variabili vedono lo stesso oggetto.

Output:

a=10 b=99
first[0]=99
s1=cat s2=cats

string è un tipo riferimento che in pratica si comporta come un valore: è immutabile, e == confronta il testo, non i riferimenti. Trovi di più sui tipi valore definiti dall'utente nelle struct.

char e bool

Un char è una unità di codice UTF-16, scritta tra apici singoli. Sotto sotto è un tipo numerico, quindi si converte in int e supporta l'aritmetica:

Output:

66
C
True
True
7
b
True

bool ha esattamente due valori e non si converte da o verso i numeri: if (count) è un errore di compilazione, non un controllo sul diverso da zero. Scrivi if (count != 0). I caratteri fuori dal Basic Multilingual Plane, come la maggior parte delle emoji, occupano due valori char (una coppia surrogata), quindi la Length di una string conta le unità UTF-16, non i caratteri visibili.

Valori predefiniti

Campi, elementi di array e default(T) ricevono il valore predefinito del tipo: zero per i numeri, false per bool, '\0' per char e null per i tipi riferimento. Le variabili locali non ricevono alcun valore predefinito e vanno assegnate prima dell'uso.

Output:

0
0
False
True
0001-01-01
0,0,0

Da C# 7.1 puoi scrivere il letterale default senza il tipo quando il compilatore riesce a dedurlo (int x = default;). Un tipo valore non può essere null, ed è a questo che serve int? (un tipo valore nullable); vedi i tipi nullable.

Interi a dimensione nativa e interi grandi

Esistono altri due tipi numerici per casi speciali. nint e nuint (C# 9) sono interi grandi quanto un puntatore, 32 o 64 bit a seconda della piattaforma, usati nell'interoperabilità e nel codice a basso livello. System.Numerics.BigInteger contiene interi di qualsiasi dimensione, a scapito della velocità:

using System.Numerics;
BigInteger factorial = 1;
for (int i = 2; i <= 30; i++) factorial *= i;
// 265252859812191058636308480000000

Domande frequenti

Quali sono i tipi di dati in C#?

I tipi predefiniti sono gli interi (sbyte, byte, short, ushort, int, uint, long, ulong), i tipi a virgola mobile float e double, decimal per l'aritmetica decimale esatta, char per un carattere UTF-16, bool e i tipi riferimento string e object. Ogni parola chiave è un alias di un tipo .NET: int è System.Int32, string è System.String.

Per il denaro in C# meglio decimal o double?

Usa decimal. Un double memorizza frazioni binarie, quindi valori come 0.1 non si possono rappresentare esattamente e le somme si discostano (0.1 + 0.2 == 0.3 è false). Un decimal memorizza cifre in base 10, mantiene da 28 a 29 cifre significative e dà i risultati che un contabile si aspetta. double è più veloce e ha un intervallo molto più ampio, quindi usalo per scienza, grafica e misurazioni.

Qual è il valore massimo di int in C#?

int.MaxValue è 2,147,483,647 e int.MinValue è -2,147,483,648 (un intero con segno a 32 bit). Sommare 1 a int.MaxValue torna silenziosamente a int.MinValue, a meno che il codice non giri in un contesto checked, dove lancia una OverflowException. Per valori più grandi usa long (fino a circa 9,2 trilioni di milioni).

Che differenza c'è tra float e double in C#?

float è a 32 bit con circa 6-9 cifre significative; double è a 64 bit con circa 15-17. I letterali a virgola mobile sono double per impostazione predefinita, quindi un float richiede il suffisso f: float speed = 2.5f;. Usa double a meno che la memoria o un'API (come Vector3 di Unity) non richieda float.

Che differenza c'è tra tipi valore e tipi riferimento in C#?

Una variabile di tipo valore (int, double, bool, DateTime, qualsiasi struct) contiene il dato stesso, e l'assegnazione lo copia. Una variabile di tipo riferimento (string, array, qualsiasi class) contiene un riferimento a un oggetto nell'heap, e l'assegnazione copia il riferimento, quindi due variabili possono puntare allo stesso oggetto.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA