CodeGym /Corsi /C# SELF /Tipi dinamici ( dynamic

Tipi dinamici ( dynamic) e DLR

C# SELF
Livello 63 , Lezione 2
Disponibile

1. Introduzione

C# è un linguaggio a tipizzazione forte. Questo significa che a compile-time una variabile deve avere un tipo definito, e si possono accedere ai membri di una classe solo in accordo con quel tipo. È sicuro, comodo, chiaro per l'IDE e per il compilatore.

Ma a volte serve lavorare con oggetti la cui struttura può essere sconosciuta a compile-time — per esempio, quando:

  • Viene eseguito codice scritto in un altro linguaggio .NET (per esempio IronPython o IronRuby), dove la tipizzazione è dinamica.
  • Si lavora con oggetti COM (per esempio con Microsoft Office tramite Interop).
  • Si interagisce con strutture dati dinamiche: ad esempio JSON poco formalizzati.
  • Non si vuole scrivere un sacco di boilerplate, è più comodo "fidarsi" dell'oggetto e provare a chiamare un metodo per nome.

Con l'introduzione in C# 4.0 della parola chiave dynamic e della piattaforma DLR è diventato possibile scrivere codice che si comporta "come in Python, ma in C#": i controlli vengono fatti a runtime e non in fase di compilazione. Con questa parola magica si può dire al compilatore di essere meno rigido e verificare l'esistenza di un metodo/proprietà solo quando il programma arriva realmente lì.

Cos'è il DLR?

Dynamic Language Runtime (DLR) è un'estensione infrastrutturale per la CLR (Common Language Runtime) che permette di implementare il supporto per linguaggi dinamici (e comportamenti dinamici in linguaggi statici). Il DLR si occupa di eseguire operazioni che non possono essere verificate dal compilatore: per esempio, chiamare un metodo per nome se il tipo sarà noto solo a runtime. Grazie al DLR, C# ottiene una "modalità dinamica".

2. La parola chiave dynamic: come funziona

Differenza rispetto a object

Alcuni principianti pensano che dynamic sia solo un modo più figo per scrivere object e fare cast ogni volta. Non è così.

  • Una variabile di tipo object richiede sempre un cast esplicito al tipo concreto per chiamare i metodi (e il compilatore lo verifica).
  • Una variabile di tipo dynamic dà un comando al compilatore: "fidati di me, so io che tipo ci sarà — vedremo a runtime!". Se il metodo non esiste — ci sarà un'eccezione a runtime.

Esempio:


// Esempio con object
object obj = "Privet, C#!";
// obj.ToUpper(); // Errore di compilazione! Il compilatore non sa che obj è una stringa
string s = ((string)obj).ToUpper(); // Solo così

// Esempio con dynamic
dynamic dyn = "Privet, C#!";
string upper = dyn.ToUpper(); // Il compilatore non obietta, il controllo avverrà a runtime

Con dynamic si può accedere a proprietà e metodi inesistenti. Se non ci sono — verrà lanciata un'eccezione di tipo RuntimeBinderException.

Uso in un esempio di applicazione

Supponiamo che nella nostra mini-app ci sia un blocco che lavora con dati JSON la cui struttura può cambiare al volo. Con dynamic si può accedere alle proprietà come se esistessero sempre:


dynamic user = new System.Dynamic.ExpandoObject();
user.Name = "Vasya";
user.Age = 35;
Console.WriteLine($"Imya: {user.Name}, vozrast: {user.Age}");

3. DLR in azione: cosa succede sotto il cofano

Quando una variabile viene dichiarata come dynamic, il compilatore C# ricorda che qualsiasi accesso a metodi, proprietà, indicizzatori, operatori, ecc. su quell'oggetto non può essere verificato in anticipo.

Al posto di ogni chiamata dinamica il compilatore inserisce codice che richiama un "dispatcher" del sistema. Il DLR verifica: esiste davvero quel metodo o quella proprietà a cui stiamo chiamando in modo ottimistico e spensierato? Se sì — esegue la chiamata, se no — lancia un'eccezione.

Schema visuale


+------------------------+
| Codice con dynamic     |
+----------+-------------+
           |
           v
+----------+-------------+
| Il compilatore C# inserisce|
| "richiesta al DLR"         |
+----------+-------------+
           |
           v
+----------+-------------+
| A runtime il DLR cerca  |
| metodo/proprietà        |
+----------+-------------+
           |
      /---------\
      |         |
   trovato   non trovato
      |         |
      v         v
  chiamata    eccezione
   metodo      (RuntimeBinderException)

Il DLR si occupa di cercare metodi e proprietà nell'oggetto — e perfino cache-a le chiamate dinamiche usate frequentemente!

Un po' di storia

Il DLR è stato creato per supportare linguaggi originariamente dinamici: IronPython, IronRuby, e per facilitare l'integrazione con oggetti dinamici in C#. Oggi è usato attivamente con ExpandoObject, DynamicObject, oltre che con COM e dati serializzati dinamicamente.

4. Confronto: static vs dynamic

Confrontiamo come appaiono le chiamate a proprietà e metodi nella paradigma statica e dinamica:

Metodo Dichiarazione Controllo a compile-time Comportamento in caso di errore
object
object obj = ...
Richiede cast al tipo giusto Errore di compilazione o InvalidCastException
dynamic
dynamic dyn = ...
No, tutto a runtime RuntimeBinderException, se il metodo non esiste
tipo statico
MyClass a = ...
Verificato dal compilatore Errore di compilazione

Esempio per chiarezza:


// Tipizzazione statica
MyUser u = new MyUser();
u.PrintInfo(); // Controllato dal compilatore

// dynamic
dynamic du = new MyUser();
du.PrintInfo(); // Il compilatore non controlla, il metodo verrà trovato solo a runtime

// object
object ou = new MyUser();
// ou.PrintInfo(); // Errore di compilazione
((MyUser)ou).PrintInfo(); // Devi castare esplicitamente

Metodi statici e dynamic


dynamic d = "Hello";
Console.WriteLine(d.Length); // 5 — tutto ok

Console.WriteLine(d.FakeMethod()); // RuntimeBinderException durante l'esecuzione

Ma attenzione! Se hai un refuso o il nome del metodo non esiste — si manifesterà solo a runtime. Esempio classico di "bug in produzione".

5. Esempi pratici: dinamica nei casi reali

Lavorare con ExpandoObject


using System.Dynamic;

dynamic person = new ExpandoObject();
person.Name = "Sergey";
person.Greet = (Action)(() => Console.WriteLine($"Privet, {person.Name}!"));

person.Greet(); // Stamperà: Privet, Sergey!

Qui abbiamo aggiunto al volo la proprietà Name e persino il delegate Greet. Molto simile a come si lavora con oggetti in JavaScript.

Binding a strutture JSON


using System.Dynamic;
using Newtonsoft.Json;

string json = @"{""name"": ""Andrey"", ""age"": 30}";
dynamic obj = JsonConvert.DeserializeObject
  
   (json); Console.WriteLine(obj.name); // Andrey Console.WriteLine(obj.age); // 30 
  

Nota: se scrivi obj.surname — otterrai un errore a runtime.

COM-Interop (Office Automation)


dynamic excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
excel.Visible = true; // "Vediamo" la proprietà anche se il compilatore non conosce il tipo

6. Sfumature utili

DLR, dynamic e uso con altri linguaggi .NET

  • IronPython/IronRuby: si possono istanziare oggetti di questi linguaggi e chiamare i loro metodi da C# tramite dynamic.
  • Oggetti COM: wrapper automatici di metodi e proprietà attraverso dynamic.
  • Oggetti dinamici custom: implementando l'interfaccia IDynamicMetaObjectProvider, puoi creare una tua classe con chiamate dinamiche.

Esempio d'integrazione con IronPython


using IronPython.Hosting;
using Microsoft.Scripting.Hosting;

ScriptEngine engine = Python.CreateEngine();
dynamic py = engine.Execute("class Test: pass\nTest()");
py.x = 42;
Console.WriteLine(py.x); // 42

Come implementare i propri tipi dinamici

Di solito è più semplice ereditare da DynamicObject.


using System.Dynamic;

class MyDynamic : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (binder.Name == "Ping")
        {
            result = "Pong!";
            return true;
        }
        result = null;
        return false;
    }
}

dynamic d = new MyDynamic();
Console.WriteLine(d.Ping); // Pong!
Console.WriteLine(d.Miss); // null (non lancia eccezione)

Quando dynamic è dannoso

  • Riduce le performance (le chiamate dinamiche sono più lente di quelle statiche).
  • Complica il debugging e la manutenzione.
  • Priva molte comodità dell'IDE: autocomplete, refactoring, ricerca riferimenti.
  • Può portare a errori che vengono scoperti solo dall'utente o in produzione.

Quando ha senso usare davvero dynamic

  • Integrazione con librerie esterne dove è impossibile o poco pratico scrivere wrapper (COM, Python, JavaScript).
  • Manipolazione di strutture debolmente tipizzate (JSON/XML/ExpandoObject).
  • Per scrivere framework, metaprogrammazione e API flessibili progettate per essere dinamiche.

In tutti gli altri casi è meglio usare tipi statici e godersi l'aiuto del compilatore :-)

7. Errori tipici lavorando con dynamic e DLR

Errore №1: Accesso a membri inesistenti.
Chiamare un metodo o una proprietà inesistente su dynamic porta a RuntimeBinderException a runtime, cosa difficile da catturare in fase di sviluppo.

Errore №2: Ignorare i controlli di tipo.
Senza verifiche di tipo prima della chiamata (per esempio, if (obj is IDictionary<string, object>)) si possono ottenere errori inattesi a runtime.

Errore №3: Usare dynamic nelle API pubbliche.
I tipi dinamici complicano la manutenzione del codice, perché IDE e analyzer non possono suggerire i membri giusti, diminuendo la leggibilità.

Errore №4: Abusare di dynamic in codice ad alte performance.
Le chiamate dinamiche sono più lente di quelle statiche e aumentano l'overhead. Usa dynamic solo quando serve.

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION