Amb això es tanca el Mòdul 2: fins ara, cada llibre de BiblioTechConsola era un grapat de
variables soltes (un string per al títol, un altre per a l'autor, un altre per a l'ISBN, un
bool per a la disponibilitat) que calia declarar i mantenir sincronitzades a mà, una vegada
per cada llibre. Si BiblioTech gestionés cent llibres, farien falta quatre-centes variables —o
quatre arrays paral·lels on la posició i de cadascun hagués de correspondre al mateix llibre,
amb el risc constant de desincronitzar-los. En aquest Mòdul 3: Programació Orientada a
Objectes resoldràs aquest problema d'arrel aprenent a definir els teus propis tipus: les
classes. Una classe agrupa en un únic lloc les dades (propietats) i, més endavant, el
comportament (mètodes) d'un concepte del món real —un llibre, un soci, un préstec—, de manera
que cada llibre de BiblioTech passi a ser un únic objecte autocontingut en lloc de quatre
variables disperses. Aquesta primera lliçó posa els fonaments: què és una classe, què és un
objecte, com es defineix una classe amb propietats, i com es creen objectes a partir d'ella amb
new.
Contingut
- De variables soltes a objectes: per què les classes
- Què és una classe? Què és un objecte?
- Definir una classe amb la paraula clau
class - Camps i propietats (
{ get; set; }) - Creant objectes amb
new - Inicialitzadors d'objecte
- Múltiples objectes de la mateixa classe
- Classes enfront de tipus primitius: tipus per referència
- De variables soltes a objectes: per què les classes
Recupera com es representava un llibre als mòduls anteriors:
string tituloLibro1 = "Cien anos de soledad";
string autorLibro1 = "Gabriel Garcia Marquez";
string isbnLibro1 = "978-84-376-0494-7";
bool disponibleLibro1 = true;
string tituloLibro2 = "Rayuela";
string autorLibro2 = "Julio Cortazar";
string isbnLibro2 = "978-84-376-0495-4";
bool disponibleLibro2 = false;Fixa't en el problema: res en el codi indica que tituloLibro1, autorLibro1, isbnLibro1 i
disponibleLibro1 pertanyen al mateix llibre; és una convenció que només existeix al cap
de qui programa, sostinguda a força de nomenar amb cura cada variable. Afegir un tercer llibre
significa copiar i enganxar quatre línies més, amb el nom Libro3, i confiar a no equivocar-se.
Aquest mòdul resol això agrupant aquestes quatre dades en un únic tipus anomenat Llibre, de
manera que cada llibre real de la biblioteca sigui representat per un sol objecte.
- Què és una classe? Què és un objecte?
Una classe és una plantilla (o "motlle") que descriu quines dades i quin comportament tindran els objectes creats a partir d'ella. No és, en si mateixa, un llibre concret: és la definició de "què és i què pot fer un llibre" al sistema. Un objecte és una instància concreta d'una classe: un llibre real, amb els seus propis valors per al títol, l'autor, l'ISBN i la disponibilitat.
La relació entre classe i objecte és la mateixa que entre un plànol d'una casa i les cases construïdes amb aquest plànol:
| Concepte | Analogia (plànol d'una casa) | A BiblioTech |
|---|---|---|
| Classe | El plànol: defineix que tota casa té portes, finestres, habitacions | Llibre: defineix que tot llibre té títol, autor, ISBN, disponibilitat |
| Objecte | Una casa concreta, construïda seguint el plànol | Un exemplar concret, per exemple "Rayuela" |
| Diversos objectes | Moltes cases diferents, construïdes amb el mateix plànol | Molts llibres diferents, tots amb la mateixa estructura |
D'un mateix plànol es poden construir tantes cases com es vulgui, cadascuna amb la seva pròpia adreça i els seus propis mobles; d'una mateixa classe es poden crear tants objectes com es vulgui, cadascun amb els seus propis valors.
- Definir una classe amb la paraula clau
class
classUna classe es defineix amb la paraula clau class seguida d'un nom (per convenció, en
PascalCase, començant per majúscula) i un cos entre claus { }:
Això ja és una classe vàlida, encara que buida: no descriu cap dada. El pas següent és indicar quina informació té cada llibre.
Convenció de noms: les classes en C# es nomenen en
PascalCase(Llibre,Soci,Prestec), a diferència de les variables i paràmetres, que fan servircamelCase(titol,numeroSoci). Aquest curs seguirà aquesta convenció de forma estricta a partir d'ara.
- Camps i propietats (
{ get; set; })
{ get; set; })Dins d'una classe, les dades es poden representar de dues formes: camps i propietats.
Un camp és la forma més simple: una variable declarada directament dins de la classe.
Tanmateix, en C# el més habitual —i el recomanat— és fer servir propietats, que es
declaren de forma semblant a un camp però inclouen { get; set; } a continuació:
class Llibre
{
public string Titol { get; set; }
public string Autor { get; set; }
public string Isbn { get; set; }
public bool Disponible { get; set; }
}Aquesta sintaxi s'anomena propietat autoimplementada (auto-implemented property): get
permet llegir el valor, i set permet assignar-lo, sense que hagis d'escriure tu el codi que
emmagatzema la dada internament (el compilador genera aquest emmagatzematge ocult per tu). A
efectes pràctics, de moment es comporten igual que un camp públic, però amb dos avantatges
importants que cobraran sentit al llarg del mòdul:
- El seu nom s'escriu en
PascalCase(Titol, notitol), seguint la convenció de C# per a tot allò visible des de fora de la classe. - Més endavant (lliçó d'Encapsulació) podràs afegir lògica de validació dins del
setsense canviar com s'utilitza la propietat des de fora —una cosa que un camp públic no permet fer de forma neta.
Camp (public string titol;) |
Propietat (public string Titol { get; set; }) |
|
|---|---|---|
| Sintaxi | Variable declarada directament | Afegeix { get; set; } |
| Convenció de nom | camelCase |
PascalCase |
| Permet afegir validació més endavant sense trencar el codi que la fa servir | No, de forma neta | Sí |
| Ús recomanat en C# per a dades exposades públicament | No | Sí |
A partir d'ara, Llibre (i la resta de classes del curs) es definiran sempre amb propietats,
no amb camps públics solts.
Aquesta serà la definició de partida de Llibre per a tot el mòdul:
class Llibre
{
public string Titol { get; set; }
public string Autor { get; set; }
public string Isbn { get; set; }
public bool Disponible { get; set; }
}
- Creant objectes amb
new
newDefinir la classe Llibre no crea encara cap llibre: només descriu com serà qualsevol llibre.
Per crear un objecte concret —una instància— s'utilitza la paraula clau new, seguida del
nom de la classe i parèntesis:
Llibre llibre1 = new Llibre();
llibre1.Titol = "Cien anos de soledad";
llibre1.Autor = "Gabriel Garcia Marquez";
llibre1.Isbn = "978-84-376-0494-7";
llibre1.Disponible = true;
Console.WriteLine(llibre1.Titol); // Cien anos de soledad
Console.WriteLine(llibre1.Disponible); // TrueAnalitzem aquesta línia clau: Llibre llibre1 = new Llibre();
Llibre(abans del nom de variable) indica el tipus de la variablellibre1: serà un objecte de tipusLlibre.new Llibre()crea un objecte nou en memòria, seguint la plantilla de la classeLlibre, i retorna una referència a ell.llibre1és la variable que guarda aquesta referència, i a partir d'aquí s'utilitza l'operador.(punt) per accedir a les seves propietats:llibre1.Titol,llibre1.Autor, etc.
Cada propietat d'un objecte acabat de crear amb new Llibre() pren el seu valor per
defecte fins que se li assigna un explícitament: null per a string (per això cal
assignar-lo abans d'usar-lo, o el programa fallarà en llegir-lo), false per a bool, 0 per
a tipus numèrics com int.
- Inicialitzadors d'objecte
Assignar cada propietat en una línia separada, com a l'exemple anterior, funciona però és
repetitiu. C# ofereix una alternativa més compacta anomenada inicialitzador d'objecte, que
assigna diverses propietats entre claus { } just després de new Llibre():
Llibre llibre2 = new Llibre
{
Titol = "Rayuela",
Autor = "Julio Cortazar",
Isbn = "978-84-376-0495-4",
Disponible = false
};
Console.WriteLine(llibre2.Titol); // RayuelaAquest codi és equivalent a crear l'objecte amb new Llibre() i després assignar cada
propietat per separat, però resulta més llegible quan s'inicialitzen diverses propietats alhora,
ja que agrupa visualment "aquests valors pertanyen a aquest objecte".
- Múltiples objectes de la mateixa classe
L'avantatge respecte a les variables soltes del Mòdul 1 s'aprecia millor amb diversos llibres
alhora: cada objecte Llibre és completament independent dels altres, encara que comparteixi
la mateixa estructura definida per la classe.
Llibre llibre1 = new Llibre { Titol = "Cien anos de soledad", Autor = "Gabriel Garcia Marquez", Isbn = "978-84-376-0494-7", Disponible = true };
Llibre llibre2 = new Llibre { Titol = "Rayuela", Autor = "Julio Cortazar", Isbn = "978-84-376-0495-4", Disponible = false };
Llibre llibre3 = new Llibre { Titol = "Ficcions", Autor = "Jorge Luis Borges", Isbn = "978-84-376-0496-1", Disponible = true };
Console.WriteLine($"{llibre1.Titol} - Disponible: {llibre1.Disponible}");
Console.WriteLine($"{llibre2.Titol} - Disponible: {llibre2.Disponible}");
Console.WriteLine($"{llibre3.Titol} - Disponible: {llibre3.Disponible}");Cadascun dels tres objectes (llibre1, llibre2, llibre3) té els seus propis valors per a
Titol, Autor, Isbn i Disponible, sense cap possibilitat de confondre el títol d'un amb
la disponibilitat d'un altre: tota la informació d'un llibre concret viu dins del seu propi
objecte. Modificar llibre2.Disponible no afecta en absolut llibre1 ni llibre3.
- Classes enfront de tipus primitius: tipus per referència
Una diferència important entre un Llibre i un tipus primitiu com int o bool és que les
classes són tipus per referència: una variable de tipus Llibre no conté l'objecte en si,
sinó una referència (una mena d'"adreça") que hi apunta en memòria. Això té una conseqüència
pràctica que convé veure des del principi:
Llibre original = new Llibre { Titol = "Rayuela", Disponible = true };
Llibre copia = original; // copia no es un llibre nou: apunta al MATEIX objecte que original
copia.Disponible = false;
Console.WriteLine(original.Disponible); // False -- tambe ha canviat l'original!Com que copia i original apunten al mateix objecte en memòria, modificar copia.Disponible
també es reflecteix en llegir original.Disponible: no són dos llibres independents, són dos
noms diferents per al mateix llibre. Això contrasta amb els tipus primitius (int, bool,
double...), que són tipus per valor: en assignar int b = a;, b rep una còpia
independent del valor d'a. Retornarem sobre aquesta distinció amb més detall a l'última
lliçó del mòdul, quan comparem classes amb struct.
Errors Comuns i Consells
- Oblidar
new: escriureLlibre llibre1;declara una variable capaç de referenciar unLlibre, però no crea cap objecte; intentar usarllibre1.Titolabans d'assignar-hinew Llibre()provoca un error de compilació (o, en certs contextos, una excepció en temps d'execució si la variable valnull). - Confondre la classe amb l'objecte:
Llibreés la plantilla;llibre1,llibre2,llibre3són objectes concrets creats a partir d'ella. És un error habitual a les primeres lliçons parlar de "la classellibre1" quan en realitat és un objecte. - Fer servir camps públics solts per costum: en C# es prefereixen les propietats
(
{ get; set; }) als camps públics, precisament perquè permeten afegir validació més endavant sense trencar el codi que ja les fa servir. Acostuma't a aquesta sintaxi des d'ara. - No caure en el fet que les classes són tipus per referència: com s'ha vist a l'últim apartat, copiar una variable de tipus classe no duplica l'objecte; totes dues variables comparteixen el mateix objecte en memòria. Aquest detall explica comportaments que, altrament, semblarien errors estranys.
- Consell: anomena les classes sempre en singular i en
PascalCase(Llibre, noLlibresnillibre): una classe descriu un únic concepte, encara que després en creïs molts objectes.
Exercicis
-
Defineix una classe
Llibreamb les propietatsTitol(string),Autor(string),Isbn(string) iDisponible(bool), totes elles com a propietats autoimplementades ({ get; set; }). -
Fent servir la classe de l'exercici anterior, crea un objecte
LlibreanomenatelMeuLlibreassignant les seves propietats una a una (sense inicialitzador d'objecte) amb les dades: títol "El Aleph", autor "Jorge Luis Borges", ISBN "978-84-376-0497-8" i disponibletrue. Mostra per consola el títol i la disponibilitat. -
Crea dos objectes
Llibrediferents fent servir l'inicialitzador d'objecte (new Llibre { ... }), amb dades diferents per a cadascun, i mostra per consola el títol de cadascun seguit de la seva disponibilitat, usant interpolació de cadenes.
Solucions
class Llibre
{
public string Titol { get; set; }
public string Autor { get; set; }
public string Isbn { get; set; }
public bool Disponible { get; set; }
}
Llibre elMeuLlibre = new Llibre();
elMeuLlibre.Titol = "El Aleph";
elMeuLlibre.Autor = "Jorge Luis Borges";
elMeuLlibre.Isbn = "978-84-376-0497-8";
elMeuLlibre.Disponible = true;
Console.WriteLine(elMeuLlibre.Titol);
Console.WriteLine(elMeuLlibre.Disponible);
Es crea l'objecte buit amb new Llibre() i després s'assigna cada propietat per separat;
al final es mostren el títol ("El Aleph") i la disponibilitat (True).
Llibre llibreA = new Llibre { Titol = "Ficcions", Autor = "Jorge Luis Borges", Isbn = "978-84-376-0496-1", Disponible = true };
Llibre llibreB = new Llibre { Titol = "Rayuela", Autor = "Julio Cortazar", Isbn = "978-84-376-0495-4", Disponible = false };
Console.WriteLine($"{llibreA.Titol} - Disponible: {llibreA.Disponible}");
Console.WriteLine($"{llibreB.Titol} - Disponible: {llibreB.Disponible}");
Cada objecte es crea i s'inicialitza en un sol pas gràcies a l'inicialitzador d'objecte; en mostrar-los per consola es comprova que cadascun manté els seus propis valors de forma independent.
Conclusió
En aquesta lliçó has fet el pas més important del mòdul: has après què és una classe, què és
un objecte, i com definir la teva primera classe pròpia, Llibre, amb propietats
autoimplementades (Titol, Autor, Isbn, Disponible). Ja saps crear objectes amb new,
assignar-los valors propietat a propietat o mitjançant inicialitzadors d'objecte, i per què les
classes són tipus per referència. Amb això, BiblioTechConsola comença a deixar enrere les
variables soltes: cada llibre és ara un objecte autocontingut.
Encara falta una peça important: de moment, un objecte Llibre és només un contenidor de
dades, sense cap comportament propi —per prestar-lo o mostrar-lo per pantalla continuem
necessitant codi extern a la classe. A la propera lliçó, Mètodes, aprendràs a afegir
comportament directament a Llibre (com Prestar(), Retornar() o MostrarFitxa()), de
manera que cada objecte sàpiga fer coses per si mateix, no només emmagatzemar dades.
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
