Tot el codi de BiblioTech escrit fins ara és fortament tipat: el compilador coneix, a cada línia, el tipus exacte de cada variable (Llibre, string, List<Soci>...) i rebutja qualsevol operació que no encaixi —accedir a una propietat inexistent, passar un argument del tipus equivocat— abans que el programa arribi a executar-se. C#, a més, ofereix un tipus especial que renuncia deliberadament a aquesta comprovació: dynamic. Aquesta lliçó explica què és, en què es diferencia d'object i de var (amb els quals es confon sovint), quan té sentit fer-lo servir, i per què el domini de BiblioTech, tipat de forma conscient des del Mòdul 1, ha de continuar evitant-lo en el seu codi de negoci.

Contingut

  1. El tipus dynamic: resolució en temps d'execució
  2. dynamic davant object: la mateixa dada, comprovació en moments diferents
  3. dynamic davant var: dinàmic no és el mateix que inferit
  4. ExpandoObject: objectes dinàmics sense una classe definida
  5. Quan té sentit fer servir dynamic
  6. Per què evitar dynamic en el domini de BiblioTech
  7. Exemple: llegir un JSON d'estructura desconeguda amb dynamic, davant l'enfocament tipat

  1. El tipus dynamic: resolució en temps d'execució

Una variable declarada com a dynamic li diu al compilador: "no comprovis res sobre aquesta variable ara; resol-ho quan el programa s'executi". Qualsevol accés a una propietat, un mètode o un operador sobre una variable dynamic es posposa fins a temps d'execució, moment en què el runtime de .NET (el Dynamic Language Runtime, DLR) intenta resoldre'l contra el tipus real del valor que conté en aquell instant:

dynamic valor = "Rayuela";
Console.WriteLine(valor.Length); // 7: en temps d'execucio, "Rayuela" es un string, que te Length

valor = 42;
Console.WriteLine(valor.Length); // Excepcio en temps d'execucio: int no te cap propietat Length

El primer Console.WriteLine compila i funciona perquè, en executar-se, valor conté un string, i string sí que té Length. El segon compila igual de bé —el compilador no comprova res sobre dynamic— però falla en temps d'execució, amb una RuntimeBinderException, perquè int no té cap propietat Length. Aquest és el cost central de dynamic: els errors que el compilador detectaria immediatament sobre qualsevol altre tipus es converteixen en excepcions que només apareixen en executar el programa, potser molt després d'haver-lo escrit.

  1. dynamic davant object: la mateixa dada, comprovació en moments diferents

object (el tipus més general del sistema de tipus de C#, Mòdul 3) i dynamic poden semblar similars —tots dos poden contenir qualsevol valor— però difereixen en quan es comprova el tipus abans d'operar-hi:

object comObject = "Rayuela";
// comObject.Length;         // Error de COMPILACIO: object no te Length
int longitud = ((string)comObject).Length; // cal un cast explicit primer

dynamic comDynamic = "Rayuela";
int longitudDinamica = comDynamic.Length;  // compila sense cast; es resol en temps d'execucio
object dynamic
Comprovació de tipus En temps de compilació (exigeix cast explícit per fer servir membres específics) Posposada a temps d'execució
Accés a membres del tipus real Requereix is/as/cast primer (Mòdul 3) Directe, sense cast, assumint que existirà
Error si el membre no existeix En temps de compilació (no compila) En temps d'execució (RuntimeBinderException)
Autocompletat de l'IDE Complet, sobre el tipus després del cast Cap: l'IDE no pot saber quins membres tindrà
Rendiment El d'una crida normal Més lent: cada operació es resol dinàmicament cada vegada

A la pràctica, object obliga a ser explícit sobre quin tipus s'espera realment (amb is/as, com al Polimorfisme del Mòdul 3) abans d'operar amb el valor; dynamic permet saltar-se aquest pas, a canvi de perdre tota garantia que el membre accedit existirà de veritat quan el programa s'executi.

  1. dynamic davant var: dinàmic no és el mateix que inferit

És un error de principiant molt comú confondre dynamic amb var (Mòdul 1), perquè tots dos permeten ometre el nom del tipus en declarar la variable. La diferència és fonamental:

var titol = "Rayuela";       // el compilador INFEREIX que titol es string, i el fixa per sempre
// titol = 42;                 // Error de COMPILACIO: no es pot assignar un int a una variable string

dynamic valorDinamic = "Rayuela"; // el compilador NO fixa cap tipus concret
valorDinamic = 42;                 // compila perfectament: dynamic pot canviar de tipus en qualsevol moment

var és sucre sintàctic pur: el compilador dedueix el tipus exacte a partir del valor inicial (aquí, string) i, a partir d'aquell moment, titol es comporta exactament com si s'hagués escrit string titol = "Rayuela"; —amb totes les comprovacions de tipus habituals. dynamic, en canvi, és un tipus real del sistema de tipus que renuncia a aquestes comprovacions: una variable dynamic pot contenir un string en una línia i un int a la següent, sense que el compilador es queixi en cap moment.

var dynamic
Què fa el compilador Infereix el tipus real una única vegada, a la declaració No fixa cap tipus; posposa tot a temps d'execució
Pot canviar de tipus la variable? No: el tipus inferit queda fix Sí: pot contenir valors de tipus diferents en moments diferents
Comprovació de tipus Completa, en temps de compilació (com qualsevol tipus explícit) Cap en temps de compilació
Cost en temps d'execució Cap (és només una declaració més curta) El de resoldre cada operació dinàmicament

  1. ExpandoObject: objectes dinàmics sense una classe definida

ExpandoObject (de System.Dynamic) porta la idea de dynamic un pas més enllà: permet crear un objecte sense cap classe que el defineixi d'antuvi, afegint-hi propietats sobre la marxa, en temps d'execució:

using System.Dynamic;

dynamic llibreInformal = new ExpandoObject();
llibreInformal.Titol = "Rayuela";
llibreInformal.Autor = "Julio Cortazar";
llibreInformal.PaginesLlegides = 120; // cap classe va declarar mai aquesta propietat

Console.WriteLine($"{llibreInformal.Titol}, pagina {llibreInformal.PaginesLlegides}");

Cap classe de BiblioTech defineix PaginesLlegides: ExpandoObject permet afegir aquesta propietat sobre la marxa perquè, per sota, no és més que un diccionari de nom-valor disfressat d'objecte amb sintaxi de punt. És útil en escenaris d'scripting o prototipatge molt ràpid, on definir una classe completa per a una estructura de dades que es fa servir una sola vegada resultaria desproporcionat; per a qualsevol dada que formi part del domini estable de BiblioTech, una classe normal (com Llibre) continua sent molt millor opció, per les raons de l'apartat 6.

  1. Quan té sentit fer servir dynamic

dynamic no és un error de disseny de C#: existeixen escenaris reals on renunciar a la comprovació de tipus en temps de compilació és, precisament, el que cal:

Escenari Per què encaixa dynamic
Interoperabilitat COM (Office, automatització d'Excel/Word des de .NET) Les APIs COM no exposen tipus estàtics que el compilador de C# pugui verificar d'antuvi
JSON no tipat d'estructura variable o desconeguda Quan no val la pena (o no és possible) definir una classe per a cada forma possible de resposta
Escenaris d'scripting o eines interactives Prototipatge ràpid on la flexibilitat importa més que la seguretat de tipus
Codi de domini tipat (BiblioTech: Llibre, Soci, Prestec...) No encaixa: vegeu l'apartat 6

  1. Per què evitar dynamic en el domini de BiblioTech

Tot el model de BiblioTech —MaterialBibliotecari, Llibre, Revista, Soci, Prestec— s'ha construït, mòdul a mòdul, recolzant-se precisament en el que dynamic descarta:

  • Seguretat de tipus en temps de compilació: llibre1.Isbn falla immediatament en escriure el codi, amb un missatge clar, si Isbn no existís o estigués mal escrit; amb dynamic llibre1, el mateix error només apareixeria com a excepció en executar aquella línia concreta, potser moltes execucions després d'haver-la escrit.
  • Autocompletat i refactorització assistida per l'IDE: l'editor coneix, en tot moment, els membres exactes de Llibre gràcies al tipatge estàtic; sobre una variable dynamic, l'IDE no pot oferir cap ajuda, perquè no sap quin tipus tindrà fins que el programa s'executi.
  • Rendiment: cada operació sobre dynamic es resol dinàmicament en temps d'execució (amb una infraestructura de resolució i una memòria cau interna del DLR), més lenta que la crida directa i ja resolta d'un tipus estàtic com Llibre.

Substituir, per exemple, Llibre llibre1 = ... per dynamic llibre1 = ... en qualsevol part de BiblioTech no aportaria cap avantatge (el tipus real sempre es coneix d'antuvi) i sí una pèrdua clara: errors que avui atura el compilador passarien a ser excepcions en temps d'execució, i l'editor deixaria d'ajudar amb autocompletat. La regla general: fes servir dynamic només quan el tipus realment no es coneix d'antuvi i no hi ha manera raonable de modelar-lo amb una classe; en qualsevol altre cas, el tipatge estàtic de C# és l'opció correcta.

  1. Exemple: llegir un JSON d'estructura desconeguda amb dynamic, davant l'enfocament tipat

La lliçó de JSON i APIs REST (Mòdul 5) va definir MetadadesLlibreExtern i Editorial per deserialitzar de forma tipada la resposta d'un servei extern de metadades de llibres, l'estructura del qual es coneixia d'antuvi. Però no totes les APIs externes documenten la seva resposta amb aquesta claredat; de vegades només es disposa d'un JSON d'exemple, d'estructura variable, sense cap classe ja escrita per a ell. dynamic, combinat amb el tipus que retorna JsonSerializer.Deserialize<dynamic> sobre un JsonElement, permet explorar-lo sense definir classes per endavant:

using System.Text.Json;

string jsonDesconegut =
    """
    {
      "isbn": "978-84-376-0495-4",
      "editorial": { "nom": "Sudamericana", "pais": "Argentina" },
      "generes": ["Novel·la", "Literatura llatinoamericana"]
    }
    """;

// Enfocament amb dynamic: sense cap classe definida d'antuvi
JsonElement arrel = JsonSerializer.Deserialize<JsonElement>(jsonDesconegut);
dynamic document = arrel;

Console.WriteLine(document.GetProperty("isbn").GetString());                          // "978-84-376-0495-4"
Console.WriteLine(document.GetProperty("editorial").GetProperty("pais").GetString()); // "Argentina"
// Enfocament tipat equivalent, com a la leccio de JSON i APIs REST (Modul 5)
MetadadesLlibreExtern? metadades =
    JsonSerializer.Deserialize<MetadadesLlibreExtern>(jsonDesconegut,
        new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });

Console.WriteLine(metadades?.Isbn);            // "978-84-376-0495-4"
Console.WriteLine(metadades?.Editorial.Pais);  // "Argentina"

Els dos fragments llegeixen la mateixa dada, però amb garanties molt diferents: document.GetProperty("isbn") no comprova en temps de compilació que la clau "isbn" existeixi al JSON —si no existís, o si s'escrivís malament ("ivbn"), l'error només apareixeria com a excepció en executar aquesta línia—; metadades?.Isbn, en canvi, és una propietat real de MetadadesLlibreExtern, verificada pel compilador, amb autocompletat a l'IDE, i amb un valor null segur i explícit si la deserialització no troba les dades esperades. dynamic pot ser raonable per a una exploració ràpida i puntual d'un JSON d'estructura encara no coneguda; tan bon punt l'estructura s'estabilitza (com ja va passar amb MetadadesLlibreExtern), migrar a classes tipades és gairebé sempre la decisió correcta per al codi que es mantindrà en el temps.

flowchart LR
    A["JSON d'estructura desconeguda"] --> B{"Es coneix la forma exacta?"}
    B -->|No, exploracio puntual| C["dynamic / JsonElement.GetProperty"]
    B -->|Si, o es pot modelar| D["Classes tipades + JsonSerializer.Deserialize-T-"]
    D --> E["Seguretat de tipus, autocompletat, millor rendiment"]

Errors Comuns i Consells

  • Confondre dynamic amb var: var és inferència de tipus en temps de compilació (el tipus queda fix); dynamic renuncia a la comprovació de tipus completament, i una mateixa variable pot canviar de tipus en temps d'execució.
  • Fer servir dynamic per evitar escriure una classe: modelar una estructura de dades estable amb una classe (com MetadadesLlibreExtern) gairebé sempre val la pena, a canvi de seguretat de tipus i autocompletat durant la resta de la vida del codi.
  • Propagar dynamic pel codi de domini: un sol dynamic en una signatura de mètode obliga que tot el codi que el faci servir després treballi també sense comprovació de tipus; convé aïllar dynamic a la vora del sistema (on de veritat es necessita, com interoperabilitat COM) i convertir a tipus concrets com més aviat millor.
  • No provar el camí d'error de dynamic: com que el compilador no avisa de res, un error amb una propietat mal escrita (document.GetProperty("ivbn")) només es descobreix executant aquesta línia exacta; sense proves que cobreixin aquest camí, l'error pot passar desapercebut fins a producció.
  • Consell: si et sorprens escrivint dynamic al codi de domini d'una aplicació com BiblioTech, és un senyal que probablement falta una classe per definir, no una raó per continuar endavant amb dynamic.

Exercicis

  1. Declara una variable dynamic valor amb el text "BiblioTech", imprimeix valor.Length, i després reassigna-li el número 2026. Explica (en un comentari) per què aquesta reassignació compila sense error, a diferència d'intentar el mateix amb una variable var.

  2. Fent servir JsonSerializer.Deserialize<JsonElement> i una variable dynamic, extreu el primer element de l'array "generes" del JSON d'exemple de l'apartat 7 (pista: document.GetProperty("generes")[0].GetString()).

  3. Explica, en un paràgraf breu, per què convertir Llibre en dynamic Llibre (és a dir, declarar dynamic llibre1 = new Llibre(...) en comptes de Llibre llibre1 = new Llibre(...)) a BiblioTech seria una mala decisió de disseny, citant almenys dues de les raons vistes a l'apartat 6.

Solucions

dynamic valor = "BiblioTech";
Console.WriteLine(valor.Length); // 10

valor = 2026; // Compila: dynamic no fixa cap tipus concret, pot contenir qualsevol valor
// Amb "var valor = "BiblioTech";", "valor = 2026;" seria un error de COMPILACIO,
// perque var infereix string una unica vegada i el tipus queda fix per sempre.
using System.Text.Json;

string jsonDesconegut =
    """
    {
      "isbn": "978-84-376-0495-4",
      "editorial": { "nom": "Sudamericana", "pais": "Argentina" },
      "generes": ["Novel·la", "Literatura llatinoamericana"]
    }
    """;

JsonElement arrel = JsonSerializer.Deserialize<JsonElement>(jsonDesconegut);
dynamic document = arrel;

Console.WriteLine(document.GetProperty("generes")[0].GetString()); // "Novel·la"

Convertir Llibre en dynamic eliminaria la comprovació en temps de compilació de tot el codi que faci servir llibre1 (un error com llibre1.Isbnn —amb errata— passaria d'un error de compilació immediat a una excepció en temps d'execució, possiblement en producció); i l'IDE deixaria de poder oferir autocompletat sobre les seves propietats i mètodes, ja que no podria saber d'antuvi quins membres tindrà llibre1. A més, cada accés als seus membres seria més lent, en resoldre's dinàmicament a cada crida en lloc d'estar ja resolt pel compilador.

Conclusió

En aquesta lliçó has vist el tipus dynamic: com posposa tota comprovació de tipus a temps d'execució, en què es diferencia d'object (que exigeix cast explícit) i de var (que infereix un tipus fix en temps de compilació), què és ExpandoObject, i en quins escenaris reals (interoperabilitat COM, JSON no tipat, scripting) té sentit fer-lo servir. També has confirmat per què el domini tipat de BiblioTech ha de continuar recolzant-se en classes concretes com Llibre en comptes de dynamic, i vist un exemple d'exploració de JSON no tipat davant l'enfocament tipat ja utilitzat al Mòdul 5.

La lliçó següent, Gestió de Memòria i Recollida d'Escombraries, deixa enrere el sistema de tipus per entrar en com .NET gestiona la memòria de qualsevol objecte —tipat o dinàmic per igual—: quina diferència hi ha entre la pila (stack) i el munt (heap), com decideix el recol·lector d'escombraries quan alliberar un objecte, i com alliberar explícitament recursos externs (fitxers, connexions) amb IDisposable, retomant el patró using ja vist al Mòdul 5.

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