A la lliçó de Polimorfisme (Mòdul 3) ja vas fer servir is Llibre llibre per comprovar el tipus real d'un MaterialBibliotecari en temps d'execució, i a Sentències Switch (Mòdul 2) vas veure un primer tast de les switch expressions. Totes dues idees formen part d'una característica més àmplia del C# modern: el pattern matching, que permet comprovar no només el tipus d'un valor, sinó també les seves propietats, els seus rangs i combinacions lògiques entre condicions, tot amb una sintaxi compacta. Aquesta lliçó reuneix el pattern matching, les switch expressions, els nullable reference types i un recordatori dels records, com el conjunt de característiques que defineixen l'estil del C# actual, i les aplica per classificar i filtrar materials de BiblioTech.

Contingut

  1. Recordatori: patrons de tipus amb is
  2. Patrons de propietat: { Disponible: true }
  3. Patrons relacionals i patrons lògics: and, or, not, >, <
  4. Switch expressions: l'evolució del switch clàssic
  5. Patrons en switch expressions: combinant tipus i propietat
  6. Nullable reference types: #nullable enable, ? i l'operador !
  7. Records com a part del C# modern: recordatori i patró posicional

  1. Recordatori: patrons de tipus amb is

A la lliçó de Polimorfisme ja vas escriure codi com aquest, fent servir un patró de tipus (type pattern):

MaterialBibliotecari material = new Llibre("Rayuela", "Julio Cortazar", "978-84-376-0495-4");

if (material is Llibre llibre)
{
    Console.WriteLine($"Es un llibre amb ISBN {llibre.Isbn}");
}

material is Llibre llibre comprova, en temps d'execució, si material és (o hereta de) Llibre, i si ho és, declara automàticament una nova variable llibre de tipus Llibre, ja convertida, disponible dins del bloc. Això ja és pattern matching: un patró de tipus és el més simple de tots els patrons que ofereix C#, i a partir d'aquí la lliçó presenta patrons més expressius que es combinen amb ell.

  1. Patrons de propietat: { Disponible: true }

Un patró de propietat (property pattern) comprova el valor d'una o més propietats d'un objecte, sense necessitat d'una variable intermèdia:

if (material is { Disponible: true })
{
    Console.WriteLine("El material esta disponible per a prestec.");
}

material is { Disponible: true } es llegeix com "si material té una propietat Disponible igual a true". Aquest patró es pot combinar amb un patró de tipus, comprovant alhora el tipus concret i una de les seves propietats:

if (material is Llibre { Disponible: true } llibre)
{
    Console.WriteLine($"'{llibre.Titol}' es un llibre disponible.");
}

Aquí es comproven, en una sola expressió, tres coses alhora: que material sigui (en temps d'execució) un Llibre, que el seu Disponible sigui true, i a més s'obté la variable llibre ja convertida, tot en una línia que abans hauria requerit un if imbricat.

  1. Patrons relacionals i patrons lògics: and, or, not, >, <

Els patrons relacionals comparen un valor numèric fent servir els operadors <, >, <=, >= directament dins d'un patró:

Revista revista1 = new Revista("National Geographic", "Varios autores", 302);

if (revista1 is Revista { NumeroEdicio: > 100 })
{
    Console.WriteLine("Revista de llarga trajectoria (mes de 100 edicions).");
}

Els patrons lògics combinen diversos patrons amb les paraules clau and, or i not (en lloc de &&, ||, !, reservats per a expressions booleanes normals):

bool esRevistaConsolidada = revista1 is Revista { NumeroEdicio: > 50 and < 1000 };

bool esDisponibleINoRevista = material is not Revista and { Disponible: true };
Patró Exemple Significat
De tipus material is Llibre És (en temps d'execució) un Llibre?
De tipus amb variable material is Llibre llibre Igual que l'anterior, i a més assigna llibre
De propietat material is { Disponible: true } La propietat Disponible val true?
Relacional numeroEdicio is > 100 El valor és més gran que 100?
Lògic and/or/not material is not Revista and { Disponible: true } Combina diversos patrons amb lògica
Combinat (tipus + propietat) material is Llibre { Disponible: true } llibre Tipus, propietat i variable alhora

Aquests patrons no es limiten a if: es poden fer servir en qualsevol expressió is, i —el més habitual en codi modern— dins d'una switch expression, que és el tema del següent apartat.

  1. Switch expressions: l'evolució del switch clàssic

A la lliçó de Sentències Switch (Mòdul 2) vas veure un primer tast de les switch expressions: una manera d'escriure un switch que retorna un valor directament, sense case/break repetits, fent servir => per a cada resultat possible:

string categoria = "Novella";

string seccio = categoria switch
{
    "Novella" => "Narrativa - Planta 1",
    "Assaig" => "No ficcio - Planta 2",
    "Poesia" => "Poesia - Planta 1",
    _ => "Categoria no reconeguda"
};
switch clàssic (sentència) Switch expression
Retorna un valor directament? No; cal assignar dins de cada case Sí; tota l'expressió s'avalua a un valor
Paraula clau per cas case valor: ... break; patro => valor,
Cas per defecte default: _ (guio baix)
Verbositat Major (claus, break a cada cas) Menor (una línia per cas, en general)
Compatible amb patrons (tipus, propietat, relacional...) Limitat Complet

La diferència més important per a aquesta lliçó és l'última fila: mentre que el switch clàssic només compara igualtat exacta contra valors constants, una switch expression admet qualsevol patró dels vistos als apartats 1 a 3, cosa que la converteix en l'eina natural per classificar objectes com els de BiblioTech segons el seu tipus real i les seves propietats.

  1. Patrons en switch expressions: combinant tipus i propietat

Així es classifica un MaterialBibliotecari combinant patrons de tipus i de propietat dins d'una switch expression:

string Classificar(MaterialBibliotecari material) => material switch
{
    Llibre { Disponible: true } => $"Llibre disponible: {material.Titol}",
    Llibre => $"Llibre prestat: {material.Titol}",
    Revista { NumeroEdicio: > 100 } => $"Revista de llarga trajectoria: {material.Titol}",
    Revista => $"Revista: {material.Titol}",
    _ => "Tipus de material no reconegut"
};
Llibre llibre1 = new Llibre("Rayuela", "Julio Cortazar", "978-84-376-0495-4");
Revista revista1 = new Revista("National Geographic", "Varios autores", 302);

llibre1.Prestar();

Console.WriteLine(Classificar(llibre1));   // "Llibre prestat: Rayuela"
Console.WriteLine(Classificar(revista1));  // "Revista de llarga trajectoria: National Geographic"

L'ordre dels casos importa, igual que en un switch clàssic: C# avalua cada patró de dalt a baix i executa el primer que coincideixi. Per això Llibre { Disponible: true } ha d'anar abans que Llibre a seques: si Llibre fos el primer cas, sempre coincidiria abans de comprovar Disponible, i el cas més específic mai s'arribaria a aplicar.

Filtrar una llista completa del catàleg reutilitza exactament el mateix patró, aquesta vegada dins d'una expressió booleana amb LINQ (que s'estudiarà en detall en una lliçó posterior d'aquest mòdul):

List<MaterialBibliotecari> catalog = new List<MaterialBibliotecari> { llibre1, revista1 };

foreach (MaterialBibliotecari material in catalog)
{
    if (material is Llibre and { Disponible: true })
    {
        Console.WriteLine($"Disponible per a prestec: {material.Titol}");
    }
}

  1. Nullable reference types: #nullable enable, ? i l'operador !

Ja vas fer servir Llibre? a la lliçó de Polimorfisme (Llibre? llibreCastejat = primerElement as Llibre;) sense aturar-te en el perquè del signe ?. El C# modern permet activar els nullable reference types, una característica que fa que el compilador distingeixi, per als tipus per referència (class, no només string/int?), entre "mai pot ser null" i "pot ser null", avisant amb advertències quan el codi no respecta aquesta distinció.

S'activa (molts projectes nous de .NET el porten activat per defecte) amb aquesta directiva al principi del fitxer:

#nullable enable

Amb aquesta directiva activa, un tipus com Llibre significa "mai null" tret que es declari explícitament Llibre?:

Llibre? CercarLlibrePerIsbn(List<Llibre> llibres, string isbn)
{
    foreach (Llibre llibre in llibres)
    {
        if (llibre.Isbn == isbn)
        {
            return llibre;
        }
    }

    return null; // valid: el tipus de retorn es Llibre?, admet null
}
Llibre? resultat = CercarLlibrePerIsbn(catalegLlibres, "978-84-376-0495-4");

if (resultat is not null)
{
    Console.WriteLine(resultat.Titol); // dins del "if", el compilador sap que no es null
}

L'operador ! (anomenat null-forgiving operator, "operador d'indulgencia amb null") li diu al compilador "confia en mi, se que aquest valor no es null aqui, encara que el seu tipus sigui nullable", silenciant l'advertència sense afegir cap comprovació real en temps d'execució:

Llibre llibreSegur = CercarLlibrePerIsbn(catalegLlibres, "978-84-376-0495-4")!;
// Advertencia silenciada, pero si el resultat fos realment null, aixo llancaria
// NullReferenceException en temps d'execucio en fer servir llibreSegur mes endavant.
Eina Què expressa
Llibre (sense ?) El compilador espera que mai sigui null; avisa si detecta el contrari
Llibre? Pot ser null; el compilador exigeix comprovar-ho abans de fer-lo servir amb confiança
is not null / is null Patró recomanat per comprovar nul·litat (més clar que != null)
valor! "Confio que no és null aquí", sense comprovació real; fes-lo servir amb moderació

És important entendre que els nullable reference types són, sobretot, una ajuda del compilador en temps de compilació (avisos, no errors durs en la majoria dels casos): no impedeixen que un null real arribi en temps d'execució si l'operador ! es fa servir de forma incorrecta, però ajuden enormement a detectar possibles NullReferenceException abans d'executar el programa.

  1. Records com a part del C# modern: recordatori i patró posicional

L'última lliçó del Mòdul 3 va presentar els record, amb ResumPrestec com a exemple:

record ResumPrestec(string TitolLlibre, string NomSoci, DateTime DataPrestec);

Els records formen part, juntament amb el pattern matching, les switch expressions i els nullable reference types, del conjunt de característiques que defineixen el C# modern: totes comparteixen l'objectiu d'expressar més significat amb menys codi, i de detectar més errors en temps de compilació. Una connexió directa entre records i pattern matching és el patró posicional, que desconstrueix un record directament en variables, fent servir el mateix ordre que el seu constructor:

ResumPrestec resum = new ResumPrestec("Rayuela", "Anna Martinez", new DateTime(2026, 1, 10));

if (resum is ResumPrestec(var titolLlibre, var nomSoci, _))
{
    Console.WriteLine($"{nomSoci} te en prestec '{titolLlibre}'");
}

ResumPrestec(var titolLlibre, var nomSoci, _) extreu les tres propietats posicionals del record en dues variables noves, descartant la tercera amb _ (el mateix guió baix que ja coneixes com a cas "per defecte" d'una switch expression, aquí fet servir com a "no m'interessa aquest valor"). Aquesta capacitat de desconstrucció automàtica és un altre avantatge que els records ofereixen "gratis", sense escriure codi addicional, gràcies a la seva sintaxi posicional.

Errors Comuns i Consells

  • Oblidar l'ordre en una switch expression amb patrons de tipus: igual que en un switch clàssic amb case apilats, un cas més general abans que un de més específic "tapa" el segon; col·loca sempre els patrons més restrictius (Llibre { Disponible: true }) abans que els més generals (Llibre).
  • Confondre and/or/not dels patrons amb &&/||/! de les expressions booleanes: són sintaxis diferents per a contextos diferents; dins d'un patró (is ..., o dins d'un case d'una switch expression) es fan servir les paraules and/or/not, no els operadors simbòlics.
  • Activar #nullable enable a mig projecte i esperar zero advertències: en activar-lo sobre codi ja existent és normal (i esperable) que apareguin avisos en llocs que necessiten revisió; no ignoris aquests avisos sistemàticament, revisa'ls un a un.
  • Abusar de l'operador !: fer-lo servir per silenciar qualsevol avís sense analitzar si el valor pot ser realment null reintrodueix, per la porta del darrere, el mateix risc de NullReferenceException que els nullable reference types estan pensats per prevenir.
  • Consell: prefereix is not null a != null (i is null a == null) en codi modern; a més de ser més llegible, is no es pot sobreescriure de forma sorprenent com sí pot passar, en tipus concrets, amb l'operador ==.

Exercicis

  1. Escriu un mètode string Classificar(MaterialBibliotecari material) que faci servir una switch expression amb patrons de tipus i de propietat per retornar: "Llibre disponible" si és un Llibre amb Disponible: true, "Llibre prestat" si és un Llibre no disponible, "Revista" en qualsevol altre cas de Revista, i "Desconegut" per a qualsevol altre tipus. Prova'l amb un Llibre disponible, un de prestat i una Revista.

  2. Activa #nullable enable al principi d'un fitxer de prova. Escriu un mètode Llibre? CercarPerTitol(List<Llibre> llibres, string titol) que retorni el primer llibre el Titol del qual coincideixi, o null si no el troba. Crida el mètode dues vegades (un títol que existeixi i un altre que no) i, en cada cas, comprova amb is not null abans d'accedir a les seves propietats.

  3. Donat un record ResumPrestec(string TitolLlibre, string NomSoci, DateTime DataPrestec), crea un objecte d'exemple i fes servir un patró posicional (is ResumPrestec(var titol, var nom, var data)) per extreure els seus tres valors en variables noves i mostrar-los per separat.

Solucions

string Classificar(MaterialBibliotecari material) => material switch
{
    Llibre { Disponible: true } => "Llibre disponible",
    Llibre => "Llibre prestat",
    Revista => "Revista",
    _ => "Desconegut"
};

Llibre llibreDisponible = new Llibre("Rayuela", "Julio Cortazar", "978-84-376-0495-4");
Llibre llibrePrestat = new Llibre("Ficcions", "Jorge Luis Borges", "978-84-376-0496-1");
llibrePrestat.Prestar();
Revista revista1 = new Revista("National Geographic", "Varios autores", 302);

Console.WriteLine(Classificar(llibreDisponible)); // "Llibre disponible"
Console.WriteLine(Classificar(llibrePrestat));    // "Llibre prestat"
Console.WriteLine(Classificar(revista1));         // "Revista"
#nullable enable

Llibre? CercarPerTitol(List<Llibre> llibres, string titol)
{
    foreach (Llibre llibre in llibres)
    {
        if (llibre.Titol == titol)
        {
            return llibre;
        }
    }
    return null;
}

List<Llibre> llibres = new List<Llibre>
{
    new Llibre("Rayuela", "Julio Cortazar", "978-84-376-0495-4")
};

Llibre? trobat = CercarPerTitol(llibres, "Rayuela");
if (trobat is not null)
{
    Console.WriteLine(trobat.Isbn); // Ok, dins del "if" ja se sap que no es null
}

Llibre? noTrobat = CercarPerTitol(llibres, "Un altre titol");
if (noTrobat is null)
{
    Console.WriteLine("No s'ha trobat el llibre.");
}
record ResumPrestec(string TitolLlibre, string NomSoci, DateTime DataPrestec);

ResumPrestec resum = new ResumPrestec("Rayuela", "Anna Martinez", new DateTime(2026, 1, 10));

if (resum is ResumPrestec(var titol, var nom, var data))
{
    Console.WriteLine($"Titol: {titol}");
    Console.WriteLine($"Soci: {nom}");
    Console.WriteLine($"Data: {data:yyyy-MM-dd}");
}

Conclusió

En aquesta lliçó has ampliat l'is i l'switch que ja coneixies amb patrons de tipus, de propietat, relacionals i lògics; has après a fer servir switch expressions amb aquests mateixos patrons per classificar objectes de forma compacta; has activat els nullable reference types perquè el compilador t'ajudi a prevenir referències nul·les; i has vist els records, ja coneguts del Mòdul 3, desconstruir-se amb un patró posicional. Tot això forma, en conjunt, l'estil idiomàtic del C# actual.

Fins ara, cada exemple de BiblioTech ha treballat amb objectes individuals o, com a molt, amb una List<MaterialBibliotecari> improvisada dins d'un mètode. La propera lliçó fa un pas enrere per introduir els genèrics: la característica que fa possible que List<T> funcioni amb qualsevol tipus, i que et permetrà escriure les teves pròpies classes i mètodes reutilitzables per a qualsevol tipus de dada, abans de construir, a la lliçó de Col·leccions, la classe Biblioteca completa.

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