Variables Locales et Expressions Lambda

This article is available in english.

Après une petite discussion avec Eric Lippert à propos d'un post sur l'utilisation dans une expression lambda d'une variable locale déclarée dans une boucle foreach, Eric m'a fait remarquer que le morceau de code suivant :

    int a = 0;
    Action action = () => Console.WriteLine(a);
    action();


N'est pas étendu par le compilateur vers ce code :

    [CompilerGenerated]
    private sealed class <>c__DisplayClass1
    {
       public int a;

       public void <Main>b__0()
       {
          Console.WriteLine(this.a);
       }
    }

    void Main()
    {

        int a = 0;

       var display = new <>c__DisplayClass1();
       display.a = a;

       var action = new Action(display.<Main>b__0);

       action();
    }

J'ai fait une supposition sur le fait qu'une variable locale était simplement copiée dans la "display class" lorsqu'elle n'est pas utilisée après la création de la lambda, ce qui n'est pas le cas.

Si l'on prend un exemple un peu différent, avec le code suivant :

    int a = 0;
    Action action = () => Console.WriteLine(a);
    a = 42;
    action();


Ma supposition aurait fait en sorte que l'exécution de ce dernier code aurait affiché 0. Or ce n'est pas correct car les expressions lambda (et les méthodes anonymes) "capturent" la variable et non la valeur; l'exécution doit donc afficher 42.

En fait, le dernier morceau de code est étendu de la manière suivante :

    var display = new <>c__DisplayClass1();

    display.a = 0;

    var action = new Action(display.<Main>b__0);

    display.a = 42;

    action();

On peut constater dans les faits que la variable qui était auparavant locale, donc sur la pile, a été "promue" en tant que champ membre de la "DisplayClass". Cela veut dire aussi que toutes les références faites à cette variable "locale", aussi bien à l'intérieur qu'à l'extérieur de la lambda, sont remplacée vers l'instance courante de "DisplayClass".

C'est finalement très simple, mais on sent que la "magie" derrière le sucre syntaxique de C#3.0 a été assez bien pensée !

Je finirais par un grand merci à Eric Lippert qui a pris le temps de me répondre, malgré qu'il soit très probablement surchargé par le développement de C# 4.0. (avec la contravariance des types génériques, yey !)
Publié vendredi 21 novembre 2008 00:51 par jay
Classé sous ,
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires


Les 10 derniers blogs postés

- SharePoint 2013: Préparation de la migration - Création des site Templates dans 2010 et 2013 par Blog Technique de Romelard Fabrice le 08-20-2014, 16:31

- [ #Yammer ] How to change interface language ? Comment changer la langue de l’interface ? par Le blog de Patrick [MVP SharePoint] le 08-20-2014, 14:21

- Onedrive Sync Engine Host : CPU à 100% par Le petit blog de Pierre / Pierre's little blog le 08-06-2014, 22:22

- SharePoint : Bug sur la gestion des permissions et la synchronisation Office par Blog Technique de Romelard Fabrice le 07-10-2014, 11:35

- SharePoint 2007 : La gestion des permissions pour les Workflows par Blog Technique de Romelard Fabrice le 07-08-2014, 11:27

- TypeMock: mock everything! par Fathi Bellahcene le 07-07-2014, 17:06

- Coding is like Read par Aurélien GALTIER le 07-01-2014, 15:30

- Mes vidéos autour des nouveautés VS 2013 par Fathi Bellahcene le 06-30-2014, 20:52

- Recherche un passionné .NET par Tkfé le 06-16-2014, 12:22

- [CodePlex] Projet KISS Workflow Foundation lancé par Blog de Jérémy Jeanson le 06-08-2014, 22:25