Syndication : Atom 1.0  RSS 2.0
Blogs des développeurs   »   Blog de Mattk

Catégorie: DotNet

02/07/2010

Permalink 14:02:49, Catégories: Récapitulatif .NET, Récapitulatif, DotNet, 635 mots   French (FR) , mattk

[.NET] Lambda Expression et Foreach

Pour ce premier article de blog sur developpez.com, j'ai décidé de m'attaquer à un problème qui m'a tenu en haleine une bonne heure : l'instanciation d'une Func via une lambda expression dans un Foreach.

Soit le code ci-dessous :

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using System.Reflection;
   6:  
   7: namespace FunctionalForeach
   8: {
   9:     public class Program
  10:     {
  11:         private static Dictionary<string, Func<string>> methods;
  12:  
  13:         public static void Main(string[] args)
  14:         {
  15:             methods = new Dictionary<string, Func<string>>();
  16:             Type type = typeof(Program);
  17:             foreach (MethodInfo method in type.GetMethods(BindingFlags.Static | BindingFlags.Public).Where(m => m.ReturnType == typeof(string)))
  18:             {
  19:                 methods.Add(method.Name, () => (string)method.Invoke(null, null));
  20:             }
  21:  
  22:             Func<string> getPseudo = methods["GetPseudo"];
  23:             Console.WriteLine(string.Format("Pseudo : {0}", getPseudo()));
  24:             Func<string> getWebsite = methods["GetWebsite"];
  25:             Console.WriteLine(string.Format("WebSite : {0}", getWebsite()));
  26:         }
  27:  
  28:         public static string GetPseudo() { return "Mattk"; }
  29:  
  30:         public static string GetWebsite() { return "http://mattk.developpez.com"; }
  31:     }
  32: }


A première vue, le résultat auquel on peut s'attendre serait le suivant :

Pseudo : Mattk 
WebSite :
http://mattk.developpez.com

Malheureusement ce ne sera pas le cas. L'exécution affichera ceci :

Pseudo: http://mattk.developpez.com
WebSite : http://mattk.developpez.com

Pour comprendre un peu ce qui se passe, regardons l’IL de la fonction Main :

Variables locales :

   1: .locals init ([0] class [mscorlib]System.Type 'type',
   2:           [1] class [mscorlib]System.Func`1<string> 'CS$<>9__CachedAnonymousMethodDelegate3',
   3:           [2] class FunctionalForeach.Program/'<>c__DisplayClass4' 'CS$<>8__locals5',
   4:           [3] class [mscorlib]System.Func`1<string> getPseudo,
   5:           [4] class [mscorlib]System.Func`1<string> getWebsite,
   6:           [5] class [mscorlib]System.Collections.Generic.IEnumerator`1<class [mscorlib]System.Reflection.MethodInfo> CS$5$0000,
   7:           [6] bool CS$4$0001)

Corps du Foreach :

   1: IL_0057:  callvirt   instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<class [mscorlib]System.Reflection.MethodInfo>::get_Current()
   2: IL_005c:  stfld      class [mscorlib]System.Reflection.MethodInfo FunctionalForeach.Program/'<>c__DisplayClass4'::'method'
   3: IL_0061:  nop
   4: IL_0062:  ldsfld     class [mscorlib]System.Collections.Generic.Dictionary`2<string,class [mscorlib]System.Func`1<string>> FunctionalForeach.Program::methods
   5: IL_0067:  ldloc.2
   6: IL_0068:  ldfld      class [mscorlib]System.Reflection.MethodInfo FunctionalForeach.Program/'<>c__DisplayClass4'::'method'
   7: IL_006d:  callvirt   instance string [mscorlib]System.Reflection.MemberInfo::get_Name()
   8: IL_0072:  ldloc.1
   9: IL_0073:  brtrue.s   IL_0084
  10: IL_0075:  ldloc.2
  11: IL_0076:  ldftn      instance string FunctionalForeach.Program/'<>c__DisplayClass4'::'<Main>b__1'()
  12: IL_007c:  newobj     instance void class [mscorlib]System.Func`1<string>::.ctor(object,
  13:                                                                                 native int)
  14: IL_0081:  stloc.1
  15: IL_0082:  br.s       IL_0084
  16: IL_0084:  ldloc.1
  17: IL_0085:  callvirt   instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,class [mscorlib]System.Func`1<string>>::Add(!0,
  18:                                                                                                                                               !1)
  19: IL_008a:  nop
  20: IL_008b:  nop
  21: IL_008c:  ldloc.s    CS$5$0000
  22: IL_008e:  callvirt   instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()

Une classe a été générée pour notre Lambda Expression, et une seule et unique variable locale (la [1]) a été déclarée (ligne 2 des variables locales).

Dans le Foreach, on peut constater qu’à chaque nouvelle instanciation d’une Func<string>, on a un stloc.1 (ligne 14) soit une assignation de la variable locale 1. Ainsi, à chaque iteration, on écrase la variable locale tout en conservant sa référence. Au final, on insére donc toujours le même objet dans notre dictionnaire methods, mais dont la valeur change à chaque itération. Ce qui provoque le comportement indésiré.

Enfin, une lambda expression entre dans le cadre de la programmation fonctionnelle. Une des règles à respecter sur une fonction (et donc une lambda) est la closure : une fonction possède son propre contexte d'exécution. L'exemple ci-dessus viole cette règle puisqu'on change le contexte (le MethodInfo passée dans la lambda) à chaque itération.

Voilà :)

Vous devez être identifié pour poster un commentaire.

Liste des blogs

Blog de Mattk

Catégories


Rechercher

<  Juin 2011  >
Lun Mar Mer Jeu Ven Sam Dim
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30      

Syndiquez ce blog XML

Articles :

Commentaires :

 
 
 
 
Partenaires

Hébergement Web