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

- Etendre le Team Web Access de TFS 2012 – Step 0 par Philippe Didiergeorges Aka Philess le 05-23-2013, 23:48

- Simuler facilement l’envoi de mail par Blog de Jérémy Jeanson le 05-22-2013, 12:52

- ProcDump 6.0 : support du filtrage sur messages d'exceptions .NET, des filtres multiples et du ciblage par nom de service par CoqBlog le 05-20-2013, 14:50

- Votez pour le TOP 10 des influenceurs SharePoint francophones ! par Le blog de Patrick [MVP SharePoint] le 05-20-2013, 12:59

- [Conf’SharePoint] Dernier rappel ! :-) par Le blog de Patrick [MVP SharePoint] le 05-20-2013, 09:09

- [ #SharePoint 2013 ] les modèles de sites standards… par Le blog de Patrick [MVP SharePoint] le 05-20-2013, 09:03

- 10 erreurs de compréhension concernant SharePoint… par Le blog de Patrick [MVP SharePoint] le 05-20-2013, 08:27

- Conf’SharePoint : 10 bonnes raisons pour ne pas la rater par Le petit blog de Pierre / Pierre's little blog le 05-14-2013, 02:24

- [Event] Soirée de lancement Agile .NET France à Lyon par Blog Agile/ALM de Vincent THAVONEKHAM le 05-13-2013, 01:29

- .NET / Debug : inspection de la mémoire d'applications .NET (dump ou processus live) : première livraison d'une librairie .NET par Microsoft par CoqBlog le 05-11-2013, 22:21