Tota aplicació necessita emmagatzemar i manipular dades: el títol d'un llibre, la quantitat de pàgines, si un exemplar està disponible o no. En C#, aquestes dades es desen en variables, i cada variable té un tipus que determina quina mena de valors pot contenir i quines operacions s'hi poden fer. En aquesta lliçó aprendràs a declarar variables, a distingir entre els tipus per valor i els tipus per referència, a convertir dades d'un tipus a un altre, a definir constants, i a entendre l'àmbit (scope) d'una variable. Farem servir sempre dades del catàleg de BiblioTech —títol, ISBN, nombre de pàgines, disponibilitat— com a fil conductor de tots els exemples.
Contingut
- Declaració de variables: tipus explícits i
var - Tipus per valor
- Tipus per referència
- Conversió de tipus
- Constants:
constireadonly - Àmbit de les variables
- Valors per defecte i nullability bàsica
Declaració de variables: tipus explícits i var
Declarar una variable en C# significa indicar al compilador tres coses: el seu tipus, el seu nom, i (opcionalment, però recomanable) un valor inicial. La sintaxi general és:
Per exemple, per representar el títol d'un llibre de BiblioTech:
string titolLlibre = "Cent anys de solitud";
int numeroPagines = 471;
bool estaDisponible = true;
Console.WriteLine(titolLlibre);
Console.WriteLine(numeroPagines);
Console.WriteLine(estaDisponible);La paraula clau var
El C# també permet declarar una variable fent servir la paraula clau var, deixant que el
compilador infereixi (dedueixi) el tipus a partir del valor assignat:
var titolLlibre = "Cent anys de solitud"; // el compilador infereix que es 'string'
var numeroPagines = 471; // el compilador infereix que es 'int'
var estaDisponible = true; // el compilador infereix que es 'bool'És molt important entendre que var no converteix el C# en un llenguatge de tipat
dinàmic: el tipus es continua fixant en temps de compilació, exactament igual que si
l'haguéssis escrit de forma explícita; simplement el compilador el dedueix per tu a partir
del valor assignat. Per això, un cop declarada amb var, la variable continua "lligada" a
aquest tipus:
var numeroPagines = 471;
// numeroPagines = "quatre-centes"; // ERROR: no es pot assignar un string a una variable inferida com a int| Aspecte | Tipus explícit (int x = 5;) |
var (var x = 5;) |
|---|---|---|
| Requereix un valor inicial? | No | Sí, sempre (el compilador necessita el valor per inferir el tipus) |
| El tipus pot canviar després? | No | No (un cop inferit, és fix) |
| Quan usar-lo | Quan el tipus aporta claredat en llegir el codi | Quan el tipus és obvi pel propi valor, o molt llarg d'escriure |
En aquest curs farem servir totes dues formes segons el context; cap és "millor" de forma
absoluta, encara que molts equips professionals prefereixen var quan el tipus és evident
pel valor assignat (com en els nostres exemples), i el tipus explícit quan ajuda a la
claredat.
Tipus per valor
Els tipus per valor (value types) són aquells on la variable conté directament la dada. Quan copies una variable de tipus valor a una altra, es copia la dada completa; cada variable és totalment independent de l'altra. Els tipus per valor més habituals en C# són els numèrics, el booleà i el caràcter:
| Tipus | Representa | Exemple | Mida aproximada |
|---|---|---|---|
int |
Nombre enter | 471 |
32 bits |
long |
Nombre enter gran | 9999999999L |
64 bits |
double |
Nombre decimal (coma flotant) | 19.99 |
64 bits |
decimal |
Nombre decimal d'alta precisió | 19.99m |
128 bits |
bool |
Valor lògic vertader/fals | true, false |
1 bit (lògic) |
char |
Un únic caràcter | 'A' |
16 bits |
Exemple aplicat a BiblioTech:
int numeroPagines = 471;
double preuLlibre = 24.90;
decimal preuExacte = 24.90m; // el sufix 'm' indica que es un 'decimal'
bool estaDisponible = true;
char inicialAutor = 'G';
Console.WriteLine(numeroPagines);
Console.WriteLine(preuLlibre);
Console.WriteLine(preuExacte);
Console.WriteLine(estaDisponible);
Console.WriteLine(inicialAutor);double o decimal?
Tots dos representen números amb decimals, però tenen un ús diferent:
doubleés més ràpid i ocupa menys espai, però pot tenir petites imprecisions en fer certes operacions (per la seva forma interna de representar els números en binari).decimalés més precís, pensat específicament per a càlculs on l'exactitud importa molt, com els diners (per exemple, el preu d'un llibre o una multa per retard a BiblioTech). El seu sufix obligatori és la lletramal final del número literal.
Com a norma pràctica: per a quantitats de diners, usa sempre decimal; per a la resta de
càlculs numèrics amb decimals, double sol ser suficient.
Tipus per referència
A diferència dels tipus per valor, els tipus per referència (reference types) no
emmagatzemen la dada directament a la variable, sinó una referència (una mena
d'"adreça") que apunta a on està realment emmagatzemada la dada. El tipus per referència
més usat, de llarg, és string:
La diferència pràctica més important entre tipus per valor i per referència apareix en copiar variables:
// Amb un tipus per VALOR (int): cada variable es independent
int paginesLlibreA = 471;
int paginesLlibreB = paginesLlibreA; // es copia el numero
paginesLlibreB = 500;
Console.WriteLine(paginesLlibreA); // continua mostrant 471: no s'ha vist afectadaAmb tipus per referència, veurem al Mòdul 3 (quan treballem amb objectes com Llibre) que
dues variables poden apuntar a la mateixa dada en memòria, de manera que modificar-ne
una a través d'una variable també afecta el que es veu a través de l'altra. string és un
cas particular de tipus per referència que es comporta, a efectes pràctics del dia a dia,
de forma molt semblant a un tipus per valor, gràcies a la seva immutabilitat (que
estudiarem en detall a la propera lliçó): un cop creada una cadena de text, el seu
contingut no es pot modificar; qualsevol operació que "sembli" modificar-la en realitat
crea una cadena nova.
| Tipus per valor | Tipus per referència | |
|---|---|---|
| Exemples | int, double, bool, char, decimal |
string, arrays, i (Mòdul 3) classes definides per nosaltres |
| Què conté la variable | La dada directament | Una referència a la dada, emmagatzemada en un altre lloc de memòria |
| En copiar una variable | Es copia la dada completa (independent) | Es copia la referència (totes dues variables poden apuntar a la mateixa dada) |
No et preocupis si aquesta distinció no queda del tot clara encara: la retrobarem amb exemples molt més rics quan treballem amb les nostres pròpies classes al Mòdul 3, on les seves implicacions es tornen més visibles.
Conversió de tipus
De vegades necessitem convertir una dada d'un tipus a un altre: per exemple, un nombre de
pàgines introduït com a text (string) necessita convertir-se a int abans de poder fer
càlculs amb ell. El C# ofereix diverses maneres de fer-ho.
Conversió implícita
Passa automàticament quan no hi ha risc de pèrdua d'informació, per exemple en passar d'un tipus "més petit" a un "més gran":
int numeroPagines = 471;
double numeroPaginesComDecimal = numeroPagines; // conversio implicita: int -> double, sense perdua de dadesConversió explícita (casting)
És necessària quan sí pot haver-hi pèrdua d'informació (per exemple, de double a
int, on es perden els decimals). S'indica escrivint el tipus destí entre parèntesis:
double preuAmbDecimals = 24.90;
int preuArrodonit = (int)preuAmbDecimals; // conversio explicita: es perd la part decimal (queda en 24)Convert i Parse/TryParse
La conversió més habitual a la pràctica és convertir text (string) a un tipus numèric,
per exemple quan es llegeix una dada introduïda per l'usuari a la consola:
string textNumeroPagines = "471";
// Opcio 1: fent servir la classe Convert
int pagines1 = Convert.ToInt32(textNumeroPagines);
// Opcio 2: fent servir el metode Parse del mateix tipus
int pagines2 = int.Parse(textNumeroPagines);
Console.WriteLine(pagines1);
Console.WriteLine(pagines2);Totes dues opcions llancen un error en temps d'execució si el text no es pot
convertir (per exemple, si conté lletres). Per evitar que el programa s'aturi
abruptament davant d'una dada no vàlida, hi ha una alternativa més segura: TryParse.
string entradaUsuari = "quatre-cents"; // un text no numeric, a mode d'exemple
bool conversioCorrecta = int.TryParse(entradaUsuari, out int pagines);
if (conversioCorrecta)
{
Console.WriteLine($"Nombre de pagines: {pagines}");
}
else
{
Console.WriteLine("El text introduit no es un numero valid.");
}TryParse retorna un valor bool indicant si la conversió ha reeixit, i lliura el
resultat mitjançant un paràmetre de sortida (out). No et preocupis si la paraula clau
out et resulta nova: de moment n'hi ha prou de saber que pagines rebrà el número
convertit només si conversioCorrecta és true. Al Mòdul 2, en veure la gestió
d'excepcions, entendràs millor per què TryParse sol ser preferible a Parse quan la
dada prové d'una font externa (com l'entrada de l'usuari) i no podem garantir que sigui
vàlida.
| Mètode | Comportament davant una dada no vàlida | Quan usar-lo |
|---|---|---|
Convert.ToInt32(text) |
Llança una excepció | Quan confies plenament en el format de la dada |
int.Parse(text) |
Llança una excepció | Similar a Convert, específic del tipus |
int.TryParse(text, out valor) |
Retorna false, no llança excepció |
Quan la dada pot no ser vàlida (per exemple, entrada d'usuari) |
Constants: const i readonly
No totes les dades han de poder canviar durant l'execució del programa. Quan un valor és fix i conegut de bestreta, és bona pràctica declarar-lo com a constant, per deixar clar que mai canviarà i perquè el mateix compilador impedeixi qualsevol intent accidental de modificar-lo.
const
S'usa per a valors que es coneixen ja en temps de compilació i mai canviaran:
const int MaxLlibresPerSoci = 5;
const string NomSistema = "BiblioTech";
Console.WriteLine($"{NomSistema} permet un maxim de {MaxLlibresPerSoci} llibres per soci.");
// MaxLlibresPerSoci = 10; // ERROR: no es pot modificar una constantreadonly
S'usa, sobretot, en el context de classes (Mòdul 3) per a valors que es fixen una única vegada, normalment en construir l'objecte, però que no es coneixen necessàriament en temps de compilació (per exemple, podrien dependre d'una data o d'una entrada externa). Aquí només el mencionem a mode de comparació; el seu ús real el veurem amb detall al Mòdul 3 quan treballem amb constructors.
const |
readonly |
|
|---|---|---|
| Es coneix el valor en... | Temps de compilació | Es pot fixar en temps d'execució (una sola vegada) |
| On s'usa típicament | Variables locals o de classe amb valor fix | Membres d'una classe (Mòdul 3) |
Àmbit de les variables
L'àmbit (scope) d'una variable és la regió del codi on aquesta variable existeix i
es pot utilitzar. En C#, l'àmbit d'una variable local ve determinat pel bloc { } en què
es declara:
{
int numeroPagines = 471; // 'numeroPagines' existeix des d'aqui...
Console.WriteLine(numeroPagines);
} // ...fins al tancament d'aquest bloc
// Console.WriteLine(numeroPagines); // ERROR: 'numeroPagines' ja no existeix fora del blocAquest comportament es tornarà molt rellevant al Mòdul 2, quan treballem amb blocs d'if
i bucles, ja que les variables declarades dins d'aquests blocs no són visibles fora d'ells.
De moment, n'hi ha prou de recordar la regla general: una variable viu dins de les claus
on va ser declarada, i desapareix en tancar-se aquest bloc.
Valors per defecte i nullability bàsica
Cada tipus per valor té un valor per defecte que rep automàticament si es declara sense inicialitzar explícitament (encara que, a la pràctica, el C# obliga a inicialitzar les variables locals abans d'usar-les, per la qual cosa aquest concepte és més rellevant en altres contextos, com els camps d'una classe que veurem al Mòdul 3):
| Tipus | Valor per defecte |
|---|---|
int, double, decimal |
0 |
bool |
false |
char |
'\0' (caràcter nul) |
string (i altres tipus per referència) |
null |
Què és null?
null representa l'absència d'un valor: una variable de tipus referència (com
string) pot no apuntar a cap dada encara. Per exemple, imaginem un llibre de BiblioTech
l'ISBN del qual encara no s'ha registrat:
string isbnLlibre = null; // el llibre existeix, pero encara no te ISBN assignat
Console.WriteLine(isbnLlibre is null); // TrueEl símbol ? per a tipus per valor nullable
Per defecte, els tipus per valor (int, bool, etc.) no poden ser null: sempre
tenen un valor concret. Tanmateix, de vegades és útil representar "encara no se sap"
fins i tot per a un número, per exemple, el nombre de pàgines d'un llibre que encara no
s'ha catalogat completament. Per a això, el C# permet convertir un tipus per valor en
nullable afegint el símbol ? després del tipus:
int? numeroPagines = null; // ara si es valid: un 'int?' pot no tenir valor
numeroPagines = 471; // pero tambe pot tenir un valor normal
Console.WriteLine(numeroPagines);Això és només una primera presa de contacte amb la idea de nullability; al Mòdul 4
aprofundirem en els tipus de referència nullable (una característica més moderna que
estén aquesta mateixa idea a string i altres tipus per referència, ajudant a prevenir
errors relacionats amb valors null inesperats). De moment, amb saber que ? després
d'un tipus per valor permet representar "sense valor encara" n'hi ha prou.
Errors Comuns i Consells
- Intentar usar una variable abans d'inicialitzar-la: el C# no permet llegir una variable local que no s'ha inicialitzat; el compilador donarà un error. Sempre assigna un valor inicial (encara que sigui temporal) abans d'usar una variable.
- Perdre precisió en usar
doubleper a diners: per la seva representació interna en binari,doublepot arrossegar petites imprecisions en càlculs amb decimals. Per a preus, multes o qualsevol quantitat monetària, usa sempredecimal. - Usar
Parseamb dades que poden no ser vàlides: si la dada prové de fora (entrada d'usuari, un fitxer, una API), usaTryParseen lloc deParseoConvert, per evitar que el programa s'aturi amb un error si la dada no té el format esperat. - Confondre
constamb una variable normal: un cop declarada una constant, qualsevol intent de modificar-la és un error de compilació; això és intencionat i forma part de la seva utilitat. - Consell: quan dubtis entre
vari un tipus explícit, pregunta't si el tipus resulta obvi només mirant el valor assignat. Si la resposta és sí,varsol fer el codi més net; si no, el tipus explícit ajuda la persona que llegeixi el codi més endavant.
Exercicis
-
Declara tres variables per representar un llibre de BiblioTech: el seu títol (
string), el seu nombre de pàgines (int) i si està disponible (bool). Fes-ho primer amb tipus explícits i després reescriu les mateixes tres declaracions fent servirvar. Imprimeix els tres valors ambConsole.WriteLine. -
Un usuari introdueix el nombre de pàgines d'un llibre com a text:
string entrada = "350";. Escriu codi que converteixi aquest text aintde forma segura fent servirTryParse, i que mostri per consola un missatge diferent segons si la conversió reeix o no. Després, prova a canviar el valor d'entradaper un text no numèric (per exemple,"tres-cents") i comprova que el missatge d'error es mostra correctament. -
Declara una constant
const int DiesPrestecEstandard = 15;que representi els dies habituals de préstec a BiblioTech. Declara també una variableint? diesExtra = null;que representarà una possible ampliació del préstec, encara no decidida. Escriu el codi necessari per, sidiesExtraté un valor assignat, sumar-lo aDiesPrestecEstandardi mostrar el resultat; si no té valor, mostrar nomésDiesPrestecEstandard. (Pista: pots comprovardiesExtra.HasValuei accedir al valor ambdiesExtra.Value, o bé usar l'operador??que assigna un valor per defecte quan alguna cosa ésnull:diesExtra ?? 0).
Solucions
-
Amb tipus explícits:
string titolLlibre = "Rayuela"; int numeroPagines = 635; bool estaDisponible = false; Console.WriteLine(titolLlibre); Console.WriteLine(numeroPagines); Console.WriteLine(estaDisponible);Amb
var(mateix resultat, tipus inferit automàticament):var titolLlibre = "Rayuela"; var numeroPagines = 635; var estaDisponible = false; Console.WriteLine(titolLlibre); Console.WriteLine(numeroPagines); Console.WriteLine(estaDisponible);
string entrada = "350";
if (int.TryParse(entrada, out int pagines))
{
Console.WriteLine($"Nombre de pagines registrat: {pagines}");
}
else
{
Console.WriteLine("El valor introduit no es un nombre de pagines valid.");
}
Si canviem entrada per "tres-cents", TryParse retornarà false i s'executarà la
branca de l'else, mostrant el missatge d'error, sense que el programa s'aturi de
forma abrupta (a diferència del que passaria amb int.Parse("tres-cents"), que
llançaria una excepció).
const int DiesPrestecEstandard = 15;
int? diesExtra = null;
int totalDies = DiesPrestecEstandard + (diesExtra ?? 0);
Console.WriteLine($"Dies totals de prestec: {totalDies}");
diesExtra = 5; // ara si s'assigna una ampliacio
totalDies = DiesPrestecEstandard + (diesExtra ?? 0);
Console.WriteLine($"Dies totals de prestec (amb ampliacio): {totalDies}");
L'operador ?? (anomenat operador de fusió amb null) retorna el valor de
l'esquerra si no és null, o el de la dreta en cas contrari. Així, quan diesExtra és
null, se suma 0; quan té un valor, se suma aquest valor.
Conclusió
En aquesta lliçó has après a declarar variables amb tipus explícits i amb var, a
distingir entre tipus per valor i tipus per referència, a convertir dades d'un tipus a un
altre de forma segura, a definir constants, a entendre l'àmbit d'una variable i a fer els
teus primers passos amb la nullability bàsica. Tots aquests conceptes són la base sobre
la qual es recolzarà absolutament tot el codi que escriguis en els propers mòduls, començant
per les classes que definirem al Mòdul 3 (Llibre, Soci, Prestec), que no són més que
un conjunt organitzat de variables (anomenades allà "camps" o "propietats") i de
comportament associat.
A la propera i última lliçó d'aquest mòdul, Arrays i Cadenes de Text, aprendràs a
treballar amb col·leccions de dades (per exemple, diversos títols de llibres a la
vegada) fent servir arrays, i a manipular text en profunditat amb la classe string,
incloent-hi interpolació, cerques i transformacions habituals.
Curs de Programació en C#
Mòdul 1: Introducció al C#
- Introducció al C#
- Configuració de l'Entorn de Desenvolupament
- Programa Hola Món
- Sintaxi i Estructura Bàsica
- Variables i Tipus de Dades
- Arrays i Cadenes de Text
Mòdul 2: Estructures de Control
Mòdul 3: Programació Orientada a Objectes
- Classes i Objectes
- Mètodes
- Constructors i Destructors
- Herència
- Polimorfisme
- Encapsulació
- Abstracció
- Structs i Records: Tipus per Valor i per Referència
Mòdul 4: Conceptes Avançats de C#
- Interfícies
- Delegats i Esdeveniments
- Pattern Matching i Característiques Modernes de C#
- Genèrics
- Col·leccions
- LINQ (Consulta Integrada en el Llenguatge)
- Programació Asíncrona
Mòdul 5: Treballant amb Dades
- Entrada/Sortida de Fitxers
- Serialització
- Connectivitat amb Bases de Dades
- Entity Framework
- Treball amb JSON i Consum d'APIs REST
Mòdul 6: Temes Avançats
- Reflexió
- Atributs
- Programació Dinàmica
- Gestió de Memòria i Recollida d'Escombraries
- Multifil i Programació Paral·lela
Mòdul 7: Construcció d'Aplicacions
Mòdul 8: Bones Pràctiques i Patrons de Disseny
- Estàndards de Codificació i Bones Pràctiques
- Patrons de Disseny
- Injecció de Dependències i Inversió de Control
- Proves Unitàries
- Revisió i Refactorització de Codi
