CodeGym /Cours /C# SELF /Types dynamiques ( dynamic) et DLR

Types dynamiques ( dynamic) et DLR

C# SELF
Niveau 63, Leçon 2
Disponible

1. Introduction

C# est un langage fortement typé. Ça veut dire qu'à la compilation une variable doit avoir un type défini, et qu'on ne peut accéder aux membres d'une classe qu'en accord avec ce type. C'est sûr, pratique, et agréable pour l'IDE et le compilateur.

Mais parfois il faut manipuler des objets dont la structure peut être inconnue à la compilation — par exemple quand :

  • On exécute du code écrit dans un autre langage .NET (par exemple IronPython ou IronRuby), où la typage est dynamique.
  • On travaille avec des objets COM (par exemple Microsoft Office via Interop).
  • On interagit avec des structures de données dynamiques : par exemple des objets JSON faiblement formalisés.
  • On ne veut pas écrire une tonne de code répétitif, et il est plus simple de "faire confiance" à l'objet et d'essayer d'appeler la méthode par son nom.

Avec l'apparition dans C# 4.0 du mot-clé dynamic et de la plateforme DLR, il est devenu possible d'écrire du code qui se comporte "comme en Python, mais en C#": la vérification se fera à l'exécution, pas à la compilation. Grâce à ce mot magique on peut demander au compilateur d'être moins strict et vérifier manuellement si la méthode/la propriété existe seulement au moment où le programme atteint ce point.

Qu'est-ce que le DLR ?

Dynamic Language Runtime (DLR) est une extension d'infrastructure pour la CLR (Common Language Runtime) qui permet d'implémenter le support des langages dynamiques (et des comportements dynamiques dans des langages statiques). Le DLR prend en charge l'exécution d'opérations qui ne peuvent pas être vérifiées par le compilateur : par exemple l'appel d'une méthode par son nom si le type ne sera connu qu'à l'exécution. Grâce au DLR, C# obtient un "mode dynamique".

2. Le mot-clé dynamic : comment il fonctionne

Différence avec object

Certains débutants pensent que dynamic est juste une façon plus à la mode d'écrire object et de faire un cast à chaque fois. Ce n'est pas le cas.

  • Une variable de type object exige toujours un cast explicite vers le type concret pour appeler des méthodes (et le compilateur le vérifie).
  • Une variable de type dynamic donne un ordre au compilateur : "fais-moi confiance, je sais quel type sera là — on réglera ça au moment voulu !". Si la méthode n'existe pas — on obtient une exception à l'exécution.

Exemple :


// Exemple avec object
object obj = "Hello, C#!";
// obj.ToUpper(); // Erreur de compilation ! Le compilateur ne sait pas que obj est une chaîne
string s = ((string)obj).ToUpper(); // Seulement comme ça

// Exemple avec dynamic
dynamic dyn = "Hello, C#!";
string upper = dyn.ToUpper(); // Le compilateur ne proteste pas, la vérification se fera à l'exécution

Avec dynamic on peut accéder à des propriétés et méthodes inexistantes. Si elles n'existent pas — une exception de type RuntimeBinderException sera levée.

Utilisation dans un exemple d'application

Supposons que dans notre mini-application il y ait un bloc qui travaille avec des données JSON dont la structure peut changer à la volée. Avec dynamic on peut accéder aux propriétés comme si elles existaient toujours :


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

3. DLR en action : que se passe-t-il sous le capot

Quand une variable est déclarée comme dynamic, le compilateur C# se souvient que tout accès aux méthodes, propriétés, indexeurs, opérateurs, etc. sur cet objet ne peut pas être vérifié à l'avance.

Au lieu de ça, à chaque appel dynamique le compilateur insère du code qui appelle un "dispatcher" du système. Le DLR vérifie : est-ce que la méthode ou la propriété à laquelle on accède de façon optimiste existe réellement ? Si oui — il effectue l'appel, sinon — il lève une exception.

Schéma visuel


+------------------------+
| Code avec dynamic      |
+----------+-------------+
           |
           v
+----------+-------------+
| Le compilateur C# insère|
| "requête au DLR"       |
+----------+-------------+
           |
           v
+----------+-------------+
| À l'exécution le DLR   |
| cherche la méthode/prop|
+----------+-------------+
           |
      /---------\
      |         |
   trouvé    pas trouvé
      |         |
      v         v
   appel      exception
   méthode    (RuntimeBinderException)

Le DLR s'occupe de chercher les méthodes et propriétés dans l'objet — et il met même en cache les appels dynamiques fréquemment utilisés !

Un peu d'histoire

Le DLR a été créé pour supporter des langages originellement dynamiques : IronPython, IronRuby, et aussi pour faciliter l'intégration avec des objets dynamiques en C#. Aujourd'hui on l'utilise activement pour travailler avec ExpandoObject, DynamicObject, ainsi qu'avec COM et des données sérialisées dynamiquement.

4. Comparaison : static vs dynamic

Comparons comment se présentent les appels de propriétés et méthodes en paradigme statique et dynamique :

Méthode Déclaration Vérification à la compilation Comportement en cas d'erreur
object
object obj = ...
Exige un cast vers le type souhaité Erreur de compilation ou InvalidCastException
dynamic
dynamic dyn = ...
Non, tout à l'exécution RuntimeBinderException, si la méthode n'existe pas
type statique
MyClass a = ...
Vérifié par le compilateur Erreur de compilation

Exemple pour illustrer :


// Typage statique
MyUser u = new MyUser();
u.PrintInfo(); // Vérifié par le compilateur

// dynamic
dynamic du = new MyUser();
du.PrintInfo(); // Le compilateur ne vérifie pas, il trouvera la méthode seulement à l'exécution

// object
object ou = new MyUser();
// ou.PrintInfo(); // Erreur de compilation
((MyUser)ou).PrintInfo(); // Il faut caster explicitement

Méthodes statiques et dynamic


dynamic d = "Hello";
Console.WriteLine(d.Length); // 5 — tout va bien

Console.WriteLine(d.FakeMethod()); // RuntimeBinderException à l'exécution

Mais il faut être prudent ! Si vous faites une faute de frappe ou si le nom de méthode n'existe pas — ça apparaîtra seulement à l'exécution. Exemple classique d'"erreur en production".

5. Exemples pratiques : le dynamique dans des tâches réelles

Travail avec ExpandoObject


using System.Dynamic;

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

person.Greet(); // Affichera : Hello, Sergey!

Ici on a ajouté à la volée la propriété Name et même le délégué Greet. Très similaire à la manipulation d'objets en JavaScript.

Liaison aux structures 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 
  

Remarquez : si vous écrivez obj.surname — ce sera une erreur à l'exécution.

COM-Interop (Office Automation)


dynamic excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
excel.Visible = true; // On "voit" la propriété, même si le compilateur ne connaît pas son type

6. Nuances utiles

DLR, dynamic et usage avec d'autres langages .NET

  • IronPython/IronRuby : on peut instancier des objets de ces langages et appeler leurs méthodes depuis C# via dynamic.
  • Objets COM : enveloppement automatique des méthodes et propriétés via dynamic.
  • Objets dynamiques personnalisés : en implémentant l'interface IDynamicMetaObjectProvider, on peut créer sa classe avec des appels dynamiques.

Exemple d'intégration avec 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

Comment implémenter ses propres types dynamiques

Généralement il est plus simple d'hériter de 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 (ne lance pas d'exception)

Quand dynamic est nocif

  • Réduit la performance (les appels dynamiques sont plus lents que les statiques).
  • Complique le débogage et la maintenance.
  • Prive la plupart des avantages de l'IDE : autocomplétion, refactoring, recherche d'usages.
  • Peut conduire à des erreurs qui ne sont détectées que par l'utilisateur ou sur le serveur.

Quand vaut-il vraiment la peine d'utiliser dynamic

  • Intégration avec des bibliothèques externes où il est impossible ou non rentable d'écrire des wrappers (COM, Python, JavaScript).
  • Manipulation de structures faiblement typées (JSON/XML/ExpandoObject).
  • Pour écrire des frameworks, de la métaprogrammation et des API flexibles qui doivent être dynamiques par conception.

Dans tous les autres cas, il est préférable d'utiliser des types statiques et de profiter de l'aide du compilateur :-)

7. Erreurs typiques en travaillant avec dynamic et DLR

Erreur n°1 : Accès à des membres inexistants.
Appeler une méthode ou propriété inexistante sur dynamic mène à RuntimeBinderException à l'exécution, ce qui est difficile à attraper pendant le développement.

Erreur n°2 : Ignorer la vérification des types.
Sans vérifier le type avant l'appel (par exemple, if (obj is IDictionary<string, object>)) on peut obtenir des erreurs inattendues à l'exécution.

Erreur n°3 : Utiliser dynamic dans des API publiques.
Les types dynamiques compliquent la maintenance du code, car l'IDE et les analyseurs ne peuvent pas suggérer les membres corrects, ce qui réduit la lisibilité.

Erreur n°4 : Abuser de dynamic dans du code performant.
Les appels dynamiques sont plus lents que les statiques et augmentent les coûts. N'utilisez dynamic que lorsque c'est nécessaire.

2
Mission
C# SELF, niveau 63, leçon 2
Bloqué
Utilisation simple de `dynamic`
Utilisation simple de `dynamic`
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION