A la lliçó anterior, cada vegada que creaves un objecte Llibre havies de recordar assignar les seves quatre propietats a mà (Titol, Autor, Isbn, Disponible) o fer servir un inicialitzador d'objecte; si oblidaves Disponible, el llibre naixia amb false (el valor per defecte de bool) en lloc de true, sense que res t'avisés de l'error. Aquesta lliçó resol aquest problema amb els constructors: un mètode especial que s'executa automàticament en l'instant en què es crea un objecte amb new, garantint que neixi sempre en un estat coherent. De passada, coneixeràs la seva contrapartida —els destructors— i ampliaràs el model de domini de BiblioTech amb dues peces noves: Soci i Prestec, que junt amb Llibre formaran el nucli sobre el qual es construirà la resta del curs.

Contingut

  1. Què és un constructor i quan s'executa
  2. El constructor per defecte
  3. Constructors amb paràmetres
  4. Sobrecàrrega de constructors
  5. Encadenar constructors amb this(...)
  6. Inicialitzadors de propietats
  7. Creant la classe Soci
  8. Unint peces: la classe Prestec
  9. Destructors i una menció a IDisposable

  1. Què és un constructor i quan s'executa

Un constructor és un mètode especial d'una classe que s'executa automàticament cada vegada que es crea un objecte amb new. Es distingeix d'un mètode normal en dos aspectes: té exactament el mateix nom que la classe, i no declara cap tipus de retorn (ni tan sols void).

class Llibre
{
    public string Titol { get; set; }
    public string Autor { get; set; }
    public string Isbn { get; set; }
    public bool Disponible { get; set; }

    public Llibre()
    {
        Disponible = true;
        Console.WriteLine("S'ha creat un nou llibre.");
    }
}
Llibre llibre1 = new Llibre(); // executa el constructor: imprimeix el missatge i posa Disponible = true
Console.WriteLine(llibre1.Disponible); // True

Tan bon punt s'executa new Llibre(), C# reserva memòria per al nou objecte i, a continuació, executa automàticament el codi del constructor —en aquest cas, assigna Disponible = true i mostra un missatge— abans de retornar la referència a l'objecte ja creat. Mai es crida un constructor "a mà" com si fos un mètode qualsevol (llibre1.Llibre() seria un error de compilació); només s'executa com a part de new.

  1. El constructor per defecte

Si una classe no defineix cap constructor propi, C# li proporciona automàticament un d'implícit, sense paràmetres, que no fa res més que crear l'objecte amb totes les seves propietats en el seu valor per defecte (com vas veure a la lliçó anterior). Això és el que ha estat passant, sense que ho sabessis, en tots els new Llibre() de les lliçons prèvies a aquesta.

Tan bon punt defineixes qualsevol constructor propi (com el de l'apartat anterior), aquest constructor implícit i gratuït desapareix: si continues necessitant poder crear un Llibre sense arguments, hauràs d'escriure tu mateix aquest constructor sense paràmetres, de forma explícita.

  1. Constructors amb paràmetres

El més habitual és que un constructor rebi paràmetres per inicialitzar l'objecte amb dades concretes des del primer moment, en lloc de crear un objecte "buit" i omplir-lo després:

class Llibre
{
    public string Titol { get; set; }
    public string Autor { get; set; }
    public string Isbn { get; set; }
    public bool Disponible { get; set; }

    public Llibre(string titol, string autor, string isbn)
    {
        Titol = titol;
        Autor = autor;
        Isbn = isbn;
        Disponible = true;
    }
}
Llibre llibre1 = new Llibre("Rayuela", "Julio Cortazar", "978-84-376-0495-4");

Console.WriteLine(llibre1.Titol);      // Rayuela
Console.WriteLine(llibre1.Disponible); // True (es fixa sempre al constructor)

Aquest constructor garanteix dues coses alhora: que no es pugui crear un Llibre sense indicar el seu títol, autor i ISBN (són paràmetres obligatoris), i que Disponible comenci sempre en true sense que qui crea el llibre hagi de recordar assignar-lo. Fixa't que, dins del constructor, els noms dels paràmetres (titol, en minúscula) són diferents dels noms de les propietats (Titol, en majúscula) precisament per poder distingir-los amb claredat en escriure Titol = titol;.

  1. Sobrecàrrega de constructors

Igual que els mètodes, els constructors es poden sobrecarregar: una mateixa classe pot tenir diversos constructors, sempre que es distingeixin per la seva llista de paràmetres.

class Llibre
{
    public string Titol { get; set; }
    public string Autor { get; set; }
    public string Isbn { get; set; }
    public bool Disponible { get; set; }

    public Llibre()
    {
        Titol = "Titol desconegut";
        Autor = "Autor desconegut";
        Isbn = "";
        Disponible = true;
    }

    public Llibre(string titol, string autor, string isbn)
    {
        Titol = titol;
        Autor = autor;
        Isbn = isbn;
        Disponible = true;
    }
}
Llibre llibreSenseDades = new Llibre();                                          // fa servir el constructor sense parametres
Llibre llibreComplet = new Llibre("Ficcions", "Jorge Luis Borges", "978-84-376-0496-1"); // fa servir el constructor amb 3 parametres

C# tria automàticament quin constructor executar segons el nombre i tipus d'arguments que li passis al new, exactament igual que amb la sobrecàrrega de mètodes vista a la lliçó anterior.

  1. Encadenar constructors amb this(...)

L'exemple anterior repeteix Disponible = true; als dos constructors: si algun dia canviés aquesta lògica d'inicialització, caldria recordar actualitzar-la als dos llocs. Per evitar aquesta duplicació, un constructor pot cridar un altre constructor de la mateixa classe usant this(...), delegant-hi la inicialització comuna:

class Llibre
{
    public string Titol { get; set; }
    public string Autor { get; set; }
    public string Isbn { get; set; }
    public bool Disponible { get; set; }

    public Llibre(string titol, string autor, string isbn)
    {
        Titol = titol;
        Autor = autor;
        Isbn = isbn;
        Disponible = true;
    }

    public Llibre() : this("Titol desconegut", "Autor desconegut", "")
    {
        // El cos pot quedar buit: this(...) ja ha fet la feina
    }
}

La sintaxi : this(...) es col·loca entre la signatura del constructor i el seu cos ({ }), i indica "abans d'executar aquest constructor, executa primer l'altre constructor amb aquests arguments". Així, new Llibre() acaba executant també la lògica del constructor de tres paràmetres, amb valors per defecte per a cadascun, sense duplicar l'assignació de Disponible. Aquesta tècnica es coneix com encadenament de constructors, i és la forma recomanada d'evitar repetir lògica d'inicialització quan una classe té diversos constructors.

  1. Inicialitzadors de propietats

Per a propietats que sempre han de començar amb el mateix valor fix, independentment del constructor que s'utilitzi, C# ofereix una alternativa encara més directa: assignar el valor per defecte en la mateixa declaració de la propietat, sense necessitat de tocar cap constructor:

class Llibre
{
    public string Titol { get; set; }
    public string Autor { get; set; }
    public string Isbn { get; set; }
    public bool Disponible { get; set; } = true; // inicialitzador de propietat

    public Llibre(string titol, string autor, string isbn)
    {
        Titol = titol;
        Autor = autor;
        Isbn = isbn;
        // Ja no cal assignar Disponible aqui: sempre comenca en true
    }
}

Un inicialitzador de propietat s'executa abans que el cos de qualsevol constructor, així que si el constructor assigna després un valor diferent a aquesta mateixa propietat, el valor del constructor preval: l'inicialitzador només actua com a valor de partida. És la forma més simple i llegible de fixar un valor per defecte quan aquest valor no depèn de cap paràmetre del constructor.

  1. Creant la classe Soci

Amb els constructors ja dominats, és el moment d'afegir la segona peça del model de BiblioTech: la classe Soci, que representa una persona registrada a la biblioteca amb dret a demanar llibres en préstec.

class Soci
{
    public int Id { get; set; }
    public string Nom { get; set; }

    public Soci(int id, string nom)
    {
        Id = id;
        Nom = nom;
    }

    public void MostrarFitxa()
    {
        Console.WriteLine($"Soci #{Id}: {Nom}");
    }
}
Soci soci1 = new Soci(1, "Ana Martinez");
Soci soci2 = new Soci(2, "Luis Gomez");

soci1.MostrarFitxa(); // Soci #1: Ana Martinez
soci2.MostrarFitxa(); // Soci #2: Luis Gomez

Soci segueix exactament el mateix patró que Llibre: propietats autoimplementades (Id, Nom), un constructor que exigeix les dades essencials en crear-lo, i un mètode (MostrarFitxa()) que aprofita aquestes dades. Id identificarà cada soci de forma única dins de BiblioTech —una idea que cobrarà més importància al Mòdul 5, en persistir dades en una base de dades.

  1. Unint peces: la classe Prestec

Amb Llibre i Soci ja definides, BiblioTech necessita representar el concepte que els relaciona: un préstec, que vincula un llibre concret amb el soci que l'ha agafat en préstec, junt amb les dates rellevants. Aquesta és la tercera i última peça del model de domini del curs:

class Prestec
{
    public Llibre Llibre { get; set; }
    public Soci Soci { get; set; }
    public DateTime DataPrestec { get; set; }
    public DateTime? DataDevolucio { get; set; } // null mentre el llibre no s'ha retornat

    public Prestec(Llibre llibre, Soci soci)
    {
        Llibre = llibre;
        Soci = soci;
        DataPrestec = DateTime.Now;
        DataDevolucio = null;
    }

    public void MostrarFitxa()
    {
        string estat = DataDevolucio is null ? "en curs" : $"retornat el {DataDevolucio:d}";
        Console.WriteLine($"Prestec de '{Llibre.Titol}' a {Soci.Nom} el {DataPrestec:d} ({estat})");
    }
}

Fixa't en tres detalls importants d'aquesta classe:

  • Les seves propietats Llibre i Soci no són tipus primitius, sinó objectes d'altres classes que ja has definit: un Prestec no duplica el títol del llibre ni el nom del soci, sinó que guarda una referència als objectes Llibre i Soci originals (recorda que les classes són tipus per referència). Si més endavant aquell llibre es marca com no disponible, el Prestec que el referencia "veu" automàticament el canvi, perquè apunta al mateix objecte.
  • DataDevolucio és de tipus DateTime? (amb ?): el signe d'interrogació indica un tipus que admet null (nullable), necessari aquí perquè, mentre el préstec continua en curs, encara no existeix cap data de devolució real.
  • El constructor fixa DataPrestec al moment actual (DateTime.Now) automàticament: qui crea un Prestec no ha de calcular ni passar la data a mà.

Així s'utilitzen les tres classes juntes, tal com es comportaran durant la resta del curs:

Llibre llibre1 = new Llibre("Rayuela", "Julio Cortazar", "978-84-376-0495-4");
Soci soci1 = new Soci(1, "Ana Martinez");

llibre1.Prestar(); // el llibre passa a Disponible = false (metode vist a la leccio anterior)
Prestec prestec1 = new Prestec(llibre1, soci1);

prestec1.MostrarFitxa(); // Prestec de 'Rayuela' a Ana Martinez el <data d'avui> (en curs)

Amb això, el model de domini de BiblioTech queda format per tres classes que col·laboren entre si: Llibre, Soci i Prestec. Aquest trio es mantindrà estable durant la resta del curs, i l'aniràs enriquint (herència, encapsulació, col·leccions, persistència...) sense canviar el seu propòsit essencial.

  1. Destructors i una menció a IDisposable

Així com un constructor s'executa en crear un objecte, un destructor (també anomenat finalitzador) és un mètode especial que el recol·lector d'escombraries (garbage collector) de .NET executa, en algun moment no determinat, abans d'alliberar definitivament la memòria d'un objecte que ja no s'utilitza. S'escriu amb el símbol ~ seguit del nom de la classe, sense modificador d'accés ni paràmetres:

class Llibre
{
    // ... propietats i constructors ...

    ~Llibre()
    {
        Console.WriteLine($"L'objecte Llibre '{Titol}' esta sent destruit.");
    }
}

A la pràctica, els destructors es fan servir molt poc en C# modern, per diverses raons: no pots controlar quan s'executen exactament (depèn del recol·lector d'escombraries, no del teu codi), afegeixen una sobrecàrrega de rendiment, i la major part de la neteja de recursos que un destructor podria fer (tancar fitxers, connexions de xarxa o de base de dades) es gestiona avui dia amb la interfície IDisposable i la instrucció using, que ofereixen un control determinista i explícit sobre quan s'allibera cada recurs. IDisposable s'estudiarà junt amb el maneig de fitxers i bases de dades al Mòdul 5; de moment n'hi ha prou que sàpigues que existeix, i que és l'alternativa preferida enfront dels destructors per alliberar recursos de forma fiable.

Constructor Destructor
Símbol Cap (mateix nom que la classe) ~ abans del nom de la classe
Quan s'executa En crear l'objecte amb new (immediat i predictible) En algun moment indeterminat, decidit pel recol·lector d'escombraries
Freqüència d'ús en C# modern Molt habitual Rara; es prefereix IDisposable (Mòdul 5)

Errors Comuns i Consells

  • Definir un constructor amb paràmetres i perdre el constructor sense paràmetres sense adonar-se'n: tan bon punt afegeixes qualsevol constructor propi, el constructor per defecte desapareix; si continues necessitant new Llibre() sense arguments, has de declarar-lo tu mateix.
  • Repetir lògica d'inicialització en diversos constructors: si dos o més constructors comparteixen part de la seva lògica, fes servir this(...) per encadenar-los en lloc de copiar i enganxar el mateix codi a cadascun.
  • Confondre el nom del paràmetre amb el de la propietat: Titol = titol; assigna a la propietat (Titol, amb majúscula) el valor del paràmetre (titol, amb minúscula); si escrivissis titol = titol; per error, no passaria res útil.
  • Fer servir DateTime en lloc de DateTime? quan el valor pot no existir encara: com que DataDevolucio pot no tenir valor mentre el préstec està en curs, ha de ser un tipus nullable (DateTime?); fer servir DateTime a seques obligaria a inventar una data "falsa" per representar "encara no retornat".
  • Confiar en el destructor per alliberar recursos importants: com que no es pot predir quan s'executarà, no és una eina fiable per tancar fitxers o connexions a temps; per a això hi ha IDisposable (Mòdul 5).

Exercicis

  1. Defineix una classe Llibre amb les propietats Titol, Autor, Isbn (totes string) i Disponible (bool, inicialitzada mitjançant un inicialitzador de propietat a true). Afegeix un únic constructor Llibre(string titol, string autor, string isbn) que assigni les tres propietats de text. Crea un objecte i comprova que Disponible val true sense haver-ho assignat al constructor.

  2. Afegeix a la classe anterior un segon constructor sense paràmetres que faci servir this(...) per encadenar amb el constructor de tres paràmetres, passant els valors "Titol desconegut", "Autor desconegut" i "". Crea un objecte amb new Llibre() i mostra el seu Titol per comprovar que l'encadenament funciona.

  3. Crea la classe Soci amb les propietats Id (int) i Nom (string) i un constructor Soci(int id, string nom). Després, crea un objecte Llibre (usant el constructor de l'exercici 1) i un objecte Soci, i utilitza tots dos per construir manualment un objecte Prestec (segons la classe definida a l'apartat 8 d'aquesta lliçó), mostrant la seva fitxa amb MostrarFitxa().

Solucions

class Llibre
{
    public string Titol { get; set; }
    public string Autor { get; set; }
    public string Isbn { get; set; }
    public bool Disponible { get; set; } = true;

    public Llibre(string titol, string autor, string isbn)
    {
        Titol = titol;
        Autor = autor;
        Isbn = isbn;
    }
}

Llibre llibre1 = new Llibre("Rayuela", "Julio Cortazar", "978-84-376-0495-4");
Console.WriteLine(llibre1.Disponible); // True
public Llibre() : this("Titol desconegut", "Autor desconegut", "")
{
}

Llibre llibreBuit = new Llibre();
Console.WriteLine(llibreBuit.Titol); // Titol desconegut

El constructor sense paràmetres delega en el de tres paràmetres mitjançant this(...), de manera que Titol, Autor i Isbn acaben rebent els valors per defecte indicats, sense duplicar codi.

class Soci
{
    public int Id { get; set; }
    public string Nom { get; set; }

    public Soci(int id, string nom)
    {
        Id = id;
        Nom = nom;
    }
}

Llibre llibre1 = new Llibre("Ficcions", "Jorge Luis Borges", "978-84-376-0496-1");
Soci soci1 = new Soci(1, "Ana Martinez");

Prestec prestec1 = new Prestec(llibre1, soci1);
prestec1.MostrarFitxa(); // Prestec de 'Ficcions' a Ana Martinez el <data d'avui> (en curs)

Conclusió

En aquesta lliçó has après a garantir que els objectes neixin sempre en un estat coherent mitjançant constructors: el constructor per defecte, constructors amb paràmetres, la seva sobrecàrrega, l'encadenament amb this(...) i els inicialitzadors de propietat; també has vist què són els destructors i per què en C# modern es prefereix IDisposable per alliberar recursos. Amb això, el model de domini de BiblioTech queda format per tres classes que col·laboren entre si: Llibre (títol, autor, ISBN, disponibilitat), Soci (identificador i nom) i Prestec (quin llibre, quin soci, quan es va prestar i quan es va retornar).

Aquest trio de classes, però, té ara mateix certa duplicació oculta: si BiblioTech volgués prestar també revistes, hauria de repetir en una nova classe Revista les mateixes propietats (Titol, Autor, Disponible) i mètodes (Prestar(), Retornar()) que ja té Llibre. A la propera lliçó, Herència, aprendràs a evitar aquesta duplicació extraient allò comú a una classe base compartida, MaterialBibliotecari, de la qual Llibre (i la nova Revista) heretaran.

Curs de Programació en C#

Mòdul 1: Introducció al C#

Mòdul 2: Estructures de Control

Mòdul 3: Programació Orientada a Objectes

Mòdul 4: Conceptes Avançats de C#

Mòdul 5: Treballant amb Dades

Mòdul 6: Temes Avançats

Mòdul 7: Construcció d'Aplicacions

Mòdul 8: Bones Pràctiques i Patrons de Disseny

Mòdul 9: Projecte Final

© Copyright 2026. Tots els drets reservats