Matthieu Mezil
C# 4 : dynamic
Avec C#4, apparaît un nouveau mot clé : dynamic. Comment peut-on l’utiliser pour simplifier nos codes ?
Par Matthieu Mezil publié le 28/10/2008 à 00:57, lu 4600 fois, 4 pages
 2 | Exemple basique

public interface I1

{

    void MyMethod();

}

 

public class C1 : I1

{

    public void MyMethod()

    {

    }

}

 

public static class MyMethodEncapsulation

{

    public static void MyMethod<T>(T item) where T : I1

    {

        item.MyMethod();

    }

}

Dans le code précédent, il est possible d’appeler MyMethod sur item car le type d’item implémente I1.
Maintenant comment faire la même chose sans I1 ? En effet, il n’est pas possible de définir une contrainte « T a une méthode MyMethod sans paramètre qui ne retourne rien ».
En C#2 (et 3), deux solutions s’offrent à nous : utiliser un délégué ou utiliser la réflection.
L’utilisation du délégué va modifier la signature de la méthode puisqu’il va falloir également passer le délégué en paramètre.
Cette façon de faire complexifie l’appel de la méthode mais assure un contrôle à la compilation.

public static void MyMethod<T>(T item, Action<T> myMethod)

{

    myMethod(item);

}

MyMethodEncapsulation.MyMethod(new C1(), c1 => c1.MyMethod());

L’utilisation de la réflection permet de simplifier l’appel à la méthode mais n’assure pas de contrôle lors de la compilation.

public static void MyMethod<T>(T item)

{

    var getMethod = typeof(T).GetMethod("MyMethod");

    if (getMethod == null)

        throw new InvalidOperationException();

    getMethod.Invoke(item, new object[0]);

}

MyMethodEncapsulation.MyMethod(new C1());

Avec C#4 et l’utilisation de dynamic, il est possible d’écrire ceci :

public static void MyMethod<T>(T item)

{

    dynamic di = item;

    di.MyMethod();

}

Ce code a l’avantage d’être plus lisible et plus concis. En revanche, tout comme l’utilisation de la réflexion, il n’assure pas de contrôle à la compilation.
On peut cependant se poser la question suivante : quel est le code réellement exécuter ?
Si on regarde avec Reflector, voici le code de MyMethod :

public static void MyMethod<T>(T item)

{

    object di = item;

    if (<MyMethod>o__SiteContainer0<T>.<>p__Site1 == null)

    {

        <MyMethod>o__SiteContainer0<T>.<>p__Site1 =

            CallSite<Action<CallSite, object>>.Create(

                new CSharpCallPayload(RuntimeBinder.GetInstance(), false, false"MyMethod", typeof(object), null));

    }

    <MyMethod>o__SiteContainer0<T>.<>p__Site1.Target(<MyMethod>o__SiteContainer0<T>.<>p__Site1, di);

}

Il y a un test sur la nullité car <MyMethod>o__SiteContainer0<T> est une classe static (et donc <>p__Site1 aussi). Cela permet de garder en cache les CallSite.
Cet article n’a pas pour vocation à rentrer en détail sur ce code mais, pour faire simple, l’idée de CallSite est d’imbriquer les délégués à exécuter.
 
» Démarrer une discussion