C# non converte mai da solo tra tipi non collegati. Una stringa che contiene cifre resta una stringa, e un double non diventa in silenzio un int. Converti in modo esplicito, e lo strumento da usare dipende da cosa stai convertendo: testo in numero (parsing), un tipo numerico in un altro (cast), oppure un oggetto in un tipo più specifico (as, is).
Da string a int: Parse, TryParse e Convert
Ci sono tre modi standard per trasformare un testo in un int:
Output:
2500
TryParse ok: 1250
TryParse failed, d = 0
42
-17
int.TryParse restituisce un bool e scrive il risultato in una variabile out, che in caso di fallimento viene impostata a 0. Dato che l'input sbagliato è normale per qualsiasi cosa digiti un utente, TryParse è la scelta predefinita per input, file e query string. Gli stessi metodi esistono su ogni tipo numerico: long.TryParse, double.TryParse, decimal.Parse, e così via.
Cosa fa ciascuno con un input sbagliato
I tre metodi differiscono solo nel modo in cui falliscono:
Output:
int.Parse(null) throws ArgumentNullException
Convert.ToInt32(null) = 0
int.Parse("abc") throws FormatException
int.Parse("12.5") throws FormatException
int.Parse("1,000") throws FormatException
int.Parse("9999999999") throws OverflowException
int.Parse("") throws FormatException
Una FormatException porta con sé il famoso messaggio "Input string was not in a correct format" (su .NET 8 e successivi, "The input string '12.5' was not in a correct format", con il testo tra virgolette), ed è uno degli errori di runtime più comuni in C#: "12.5" non è un int, e nemmeno "1,000". Per accettarli, analizzali come decimal, oppure di' al parser quali stili sono consentiti:
Output:
1000
255
13
10
Cultura: perché "1.5" può diventare 15
Per impostazione predefinita il parsing usa la cultura corrente, e le culture non concordano sui separatori. In tedesco, in italiano e in diverse altre culture europee il separatore decimale è la virgola e il punto raggruppa le migliaia. La stessa stringa dà numeri diversi:
Output:
1.5
15
1.5
Il codice che legge file di dati, JSON, configurazione o qualsiasi cosa generata da una macchina dovrebbe passare sempre CultureInfo.InvariantCulture, sia nel parsing sia nella formattazione. Altrimenti lo stesso programma funziona sulla macchina di uno sviluppatore e legge male ogni numero su un server impostato in un'altra lingua. Usa la cultura corrente solo per il testo che una persona ha digitato o leggerà.
Conversioni numeriche implicite ed esplicite
Le conversioni di ampliamento avvengono in modo implicito: da int a long, da int a double, da float a double, da int a decimal. Una conversione che può perdere l'ordine di grandezza del valore o la sua parte frazionaria richiede un cast esplicito (type)value. Un'eccezione da tenere a mente: da int o long a float, e da long a double, sono implicite anche se possono perdere precisione: float f = 16_777_217; compila e memorizza 16777216.
Output:
9 -9
10
2
4
705032704
9.99
7 7.0 7
Tre cose da ricordare. Un cast da virgola mobile a intero tronca; non arrotonda. Convert.ToInt32(double) arrotonda, con l'arrotondamento bancario (le metà vanno al vicino pari). E un cast intero che non ci sta si riavvolge in silenzio, a meno che non giri in un contesto checked, dove lancia OverflowException (vedi tipi di dati).
int x = 3.7; è un errore di compilazione (CS0266: cannot implicitly convert type 'double' to 'int', an explicit conversion exists): è il compilatore che ti chiede di scegliere tra un cast e un metodo di arrotondamento.
Numeri e altri tipi in string
Ogni tipo ha ToString(), e i tipi numerici accettano una stringa di formato. L'interpolazione di stringhe usa la stessa formattazione:
Output:
42
000042
1234.50
1,234.50
$1,234.50
FF
1010
True
Order 000042: 1234.50
I codici di formato (D, F, N, C, X, P, E) e i pattern personalizzati come "0.00" sono spiegati in interpolazione di stringhe.
Caratteri e booleani
Output:
7
55
7
7
A
True
False
1
False
(int)'7' vale 55, il codice del carattere, non 7. Questo inganna chiunque sommi le cifre di un numero; sottrai '0' o usa char.GetNumericValue. bool.Parse accetta "true" e "false" con qualsiasi combinazione di maiuscole e minuscole, e nient'altro ("yes" e "1" lanciano un'eccezione).
Conversioni di riferimento: cast, as e is
Per i tipi classe, un cast verifica a runtime il tipo effettivo dell'oggetto. L'upcast verso una classe base è implicito. Il downcast verso una classe derivata richiede un cast esplicito, che lancia InvalidCastException quando l'oggetto è qualcos'altro:
Output:
4242
True
Card ending 4242, 50.00
InvalidCastException
5
cannot unbox int as long
Preferisci is con una variabile quando devi decidere in base al tipo; è un solo controllo, non può lanciare eccezioni, e la nuova variabile esiste solo dove il test è riuscito. Usa un cast semplice quando un tipo diverso sarebbe un bug che deve fallire in modo evidente. Il caso dell'int inscatolato inganna chi legge numeri da collezioni di object o da celle di DataRow: prima fai l'unboxing al tipo esatto ((long)(int)boxed), oppure usa Convert.ToInt64(boxed), che gestisce qualsiasi tipo numerico.
Domande frequenti
Come converto una stringa in int in C#?
Usa int.TryParse(text, out int number) quando il testo arriva da un utente, da un file o dalla rete: restituisce false invece di lanciare un'eccezione quando il testo non è un numero valido. Usa int.Parse(text) quando un testo non valido è un bug e un'eccezione è la risposta giusta. Convert.ToInt32(text) si comporta come int.Parse, tranne che restituisce 0 per null.
Qual è la differenza tra int.Parse e Convert.ToInt32?
Per le stringhe sono quasi uguali: entrambi lanciano FormatException su un testo come "abc" e OverflowException su numeri fuori intervallo. La differenza è null: int.Parse(null) lancia ArgumentNullException, mentre Convert.ToInt32(null) restituisce 0. Convert.ToInt32 accetta anche altri tipi (double, bool, object), e arrotonda un double all'intero pari più vicino invece di troncarlo.
Cosa significa "Input string was not in a correct format"?
È il messaggio di una FormatException lanciata da int.Parse, double.Parse o Convert.ToInt32 (su .NET 8 e successivi dice "The input string 'abc' was not in a correct format", citando il testo) quando il testo non è un numero nel formato atteso: lettere, una stringa vuota, un punto decimale quando analizzi un int, o un separatore delle migliaia. Passa a TryParse per gestire l'input sbagliato senza eccezioni, e controlla in quale cultura è stato scritto il numero.
Come faccio il cast di un double a int in C#?
Scrivi un cast esplicito: int n = (int)price;. Il cast tronca verso zero, quindi (int)9.99 è 9 e (int)-9.99 è -9. Per arrotondare, invece, usa (int)Math.Round(price), e tieni presente che per impostazione predefinita Math.Round arrotonda le metà al numero pari più vicino; passa MidpointRounding.AwayFromZero per l'arrotondamento che si impara a scuola.
Qual è la differenza tra as e un cast in C#?
Un cast (Dog)animal lancia InvalidCastException se l'oggetto non è un Dog. animal as Dog restituisce invece null, quindi va seguito da un controllo sul null, e funziona solo con tipi riferimento e nullable. L'alternativa moderna è il pattern is: if (animal is Dog dog) { ... } verifica e converte in un solo passaggio.