Variabili, tipi di dato e conversioni in C#
Ogni programma manipola dati, e in C# ogni dato ha un tipo ben preciso. Essendo un linguaggio fortemente tipizzato, C# richiede che ogni variabile sia associata a un tipo che ne definisce i valori ammessi, la dimensione in memoria e le operazioni possibili. Questa rigidità, lungi dall'essere un ostacolo, è una delle principali fonti di sicurezza del linguaggio: molti errori vengono individuati già in fase di compilazione, prima ancora di eseguire il programma. In questo articolo vediamo come dichiarare variabili, quali tipi offre il linguaggio e come convertire un valore da un tipo all'altro.
Dichiarare una variabile
Una variabile è un'area di memoria con un nome, un tipo e un valore. La dichiarazione più esplicita indica il tipo, seguito dal nome e da un valore iniziale opzionale:
// Dichiarazione con tipo esplicito
int age = 30;
string name = "Gabriele";
bool isActive = true;
double price = 19.99;
In alternativa, quando il tipo è deducibile dal valore assegnato, si può usare la parola chiave var e lasciare che sia il compilatore a determinarlo. È importante sottolineare che var non significa "tipo dinamico": il tipo viene comunque fissato in fase di compilazione e non può cambiare in seguito.
// Il compilatore deduce int, string, bool, double
var age = 30;
var name = "Gabriele";
var isActive = true;
var price = 19.99;
La scelta tra tipo esplicito e var è spesso questione di leggibilità: si preferisce var quando il tipo è evidente dal lato destro, mentre si mantiene il tipo esplicito quando aiuta a chiarire l'intento del codice.
Tipi valore e tipi riferimento
In C# i tipi si dividono in due grandi categorie che determinano il modo in cui i dati vengono memorizzati e passati. Comprendere questa distinzione è fondamentale per evitare comportamenti inattesi.
- Tipi valore: contengono direttamente il dato. Quando si assegna un tipo valore a un'altra variabile, se ne crea una copia indipendente. Ne fanno parte i tipi numerici,
bool,char, le enumerazioni e le strutture. - Tipi riferimento: contengono un riferimento alla posizione in memoria dove risiede il dato. Assegnando un tipo riferimento a un'altra variabile, entrambe puntano allo stesso oggetto. Ne fanno parte le classi, le stringhe, gli array e le interfacce.
// Tipo valore: x e y sono indipendenti
int x = 5;
int y = x;
y = 10;
Console.WriteLine(x); // Stampa 5, x non e cambiato
// Tipo riferimento: entrambi puntano allo stesso array
int[] first = { 1, 2, 3 };
int[] second = first;
second[0] = 99;
Console.WriteLine(first[0]); // Stampa 99, e lo stesso oggetto
I tipi numerici
C# offre numerosi tipi numerici, che si distinguono per dimensione e per la capacità di rappresentare numeri interi o decimali. La scelta del tipo dipende dall'intervallo di valori da rappresentare e dalla precisione richiesta.
| Tipo | Descrizione | Esempio |
|---|---|---|
int | Intero con segno a 32 bit | -2.147.483.648 a 2.147.483.647 |
long | Intero con segno a 64 bit | Numeri molto grandi |
double | Virgola mobile a 64 bit | Uso generale per i decimali |
float | Virgola mobile a 32 bit | Minor precisione, meno memoria |
decimal | Decimale ad alta precisione | Calcoli finanziari |
Un dettaglio importante riguarda i letterali. Un numero decimale scritto direttamente nel codice è interpretato come double, mentre per indicare esplicitamente float o decimal si usano i suffissi f e m:
double d = 3.14; // double per impostazione predefinita
float f = 3.14f; // il suffisso f indica un float
decimal m = 3.14m; // il suffisso m indica un decimal, ideale per il denaro
Booleani, caratteri e stringhe
Oltre ai numeri, i tipi di base includono bool per i valori logici, char per un singolo carattere Unicode e string per sequenze di testo. I letterali di carattere usano gli apici singoli, mentre le stringhe usano gli apici doppi.
bool isValid = false;
char grade = 'A';
string message = "Benvenuto in C#";
// Le stringhe supportano l'interpolazione con il prefisso $
string userName = "Gabriele";
string greeting = $"Ciao, {userName}!";
Console.WriteLine(greeting); // Stampa: Ciao, Gabriele!
Costanti e valori di sola lettura
A volte un valore non deve mai cambiare. Per questo scopo C# offre due meccanismi. La parola chiave const definisce una costante il cui valore è noto in fase di compilazione, mentre readonly definisce un campo che può essere assegnato solo in fase di inizializzazione o nel costruttore.
// Costante nota a tempo di compilazione
const double Pi = 3.14159;
// Il tentativo di riassegnare una costante genera un errore di compilazione
// Pi = 3.14; // ERRORE
Tipi nullable
I tipi valore normalmente non possono contenere l'assenza di valore. Per rappresentare esplicitamente "nessun valore" si usano i tipi nullable, indicati aggiungendo un punto interrogativo dopo il tipo. Questo è utile, ad esempio, quando un dato proveniente da un database potrebbe non essere presente.
// Un intero che puo anche essere assente
int? optionalAge = null;
if (optionalAge.HasValue)
{
Console.WriteLine(optionalAge.Value);
}
else
{
Console.WriteLine("Eta non specificata");
}
// L'operatore null-coalescing fornisce un valore predefinito
int actualAge = optionalAge ?? 0;
Conversioni di tipo
Spesso è necessario passare da un tipo a un altro. C# distingue tra conversioni implicite, sicure e automatiche, e conversioni esplicite, che richiedono un cast perché potrebbero comportare una perdita di dati.
// Conversione implicita: da int a double non si perde nulla
int whole = 42;
double asDouble = whole;
// Conversione esplicita con cast: da double a int si perde la parte decimale
double value = 9.99;
int truncated = (int)value; // truncated vale 9
Quando si lavora con dati testuali, ad esempio l'input di un utente, occorre trasformare le stringhe in numeri. Il metodo consigliato è TryParse, che tenta la conversione e restituisce un valore booleano di successo senza sollevare eccezioni in caso di formato non valido:
string input = "123";
// TryParse restituisce true e assegna il risultato tramite out
if (int.TryParse(input, out int number))
{
Console.WriteLine($"Numero valido: {number}");
}
else
{
Console.WriteLine("Formato non valido");
}
In alternativa esistono int.Parse, che solleva un'eccezione se la stringa non è convertibile, e la classe Convert, che offre metodi come Convert.ToInt32 per convertire tra i vari tipi di base.
Conclusione
Abbiamo esplorato il sistema dei tipi di C#, distinguendo tra tipi valore e tipi riferimento e imparando a dichiarare variabili con tipo esplicito o con var. Abbiamo passato in rassegna i tipi numerici, i booleani, i caratteri e le stringhe, e visto come proteggere i valori con const e readonly, come rappresentare l'assenza di valore con i tipi nullable e come convertire i dati in modo sicuro. Nel prossimo articolo useremo questi tipi all'interno di operatori e strutture di controllo, per dare al nostro codice la capacità di prendere decisioni e ripetere operazioni.