Simon on the (dot)Net

L'homme qui codait en do dièse

  Home :: Contact :: Syndication  :: Login
  29 Posts :: 0 Stories :: 0 Comments :: 0 Trackbacks

News

Archives

Image Galleries

mardi 8 août 2006 #

Thomas Lebrun vient de publier un article très intéressant sur les nouveautés de C# 3. Il couvre à peu prêt tout ce que l’on sait (et que l’on peut tester) pour l’instant sur la nouvelle version du langage ainsi que des technologies qui lui sont associées.

Seulement en parcourant l’article j’ai un peu buté sur un paragraphe à propos des expressions Lambda. En effet, d’après certaines tournures de phrase, on pourrait croire que les expressions Lambda ne sont que des méthodes anonymes dont on aurait raccourci la syntaxe… Ce qui est vrai dans certains cas (pour Linq for Objects par exemple), mais qui est loin d’être une généralité.

En effet le principal apport des expressions Lambda est justement la notion d’"expression". Une expression diffère d’une instruction par le fait qu’elle peut être évaluée et interprétée par le code. On parle alors d’évaluation d’arbre d’expressions.

Une expression Lambda sera donc suivant les cas compilée en tant que méthode anonyme (quand la méthode dans laquelle elle est utilisée attend un délégué), ou comme une "expression" lorsque la méthode attend une expression.

Pour illustrer ceci, voici un petit exemple de code :

DataContext1 dc = new DataContext1();
var allresults = from p in dc.MyPersistentTypes
select p;

Console.WriteLine("Copy all entities from database to a list");
 
List<MyPersistentType> allEntitiesInAList = new List<MyPersistentType>(allresults);

Console.WriteLine("Query from table :");

var queryFromTable = from p in dc.MyPersistentTypes
                     where p.Id > 2
                     select p;
 
 
Console.WriteLine("Query from a list :");
var queryFromList = from p in allEntitiesInAList
                    where p.Id > 2
                    select p;

A noter ici que j’utilise la syntaxe Linq plutôt qu’une expression Lambda, ce qui revient exactement au même.

Voyons maintenant le résultat de la décompilation du code généré grâce à l’outil Reflector :


DataContext1 context1 = new DataContext1();
Table<MyPersistentType> table1 = context1.MyPersistentTypes;
 
Console.WriteLine("Copy all entities from database to a list");
 
List<MyPersistentType> list1 = new List<MyPersistentType>(table1);
 
Console.WriteLine("Query from table :");
 
ParameterExpression expression1 = Expression.Parameter(typeof(MyPersistentType), "p");
IQueryable<MyPersistentType> queryable1 = 
     Queryable.Where<MyPersistentType>(context1.MyPersistentTypes,
           Expression.Lambda<Func<MyPersistentType,bool>>(
                Expression.GT(Expression.Property(expression1,
                      (MethodInfo) methodof(MyPersistentType.get_Id)),
                       Expression.Constant(2, typeof(int))),
                new ParameterExpression[] { expression1 }));
 
Console.WriteLine("Query from a list :");

if (Program.<>9__CachedAnonymousMethodDelegate5 == null)
{
   Program.<>9__CachedAnonymousMethodDelegate5 = new Func<MyPersistentType, bool>(Program.

b__3);
}
IEnumerable
<MyPersistentType> enumerable1 = Sequence.Where<MyPersistentType>(list1, Program.<>9__CachedAnonymousMethodDelegate5);

On voit bien ici que la même expression lambda est compilée de façon différente selon le cas dans lequel elle est utilisée. Pour le type Table, on a droit à quelque chose relativement compliqué en apparence (il s’agit en fait d’un arbre d’expression), alors que pour le type List, on utilise un délégué généré (de la même facon qu’une méthode anonyme).

En fait, le compilateur a détecté que le type Table implémente IQueryable (et plus précisément IQueryable<MyPersistantType>), et a donc décidé de créer un arbre d’expressions qui sera évalué par le provider utilisé (définit au niveau du DataContext dans notre cas), afin d’obtenir le résultat escompté (dans notre cas, ceci éxécutera une requete SQL du genre « SELECT * from MyTable where ID > 2 » et nous la renverra sous la forme d’une énumération de MyPersistentType). A noté que l’arbre d’expression n’est pas évalué dans notre cas, car nous n’avons pas utilisé le résultat de la requête dans notre code. En effet, l’éxécution de la requête ne se fait qu’au moment où queryFromTable est utilisé.

Je ne détaillerai pas l’arbre d’expression généré ici, dans les grande lignes, on peut voir que l’on passe à la méthode Where une lambda expression (Expression.Lambda), constituée d’une comparaison "Greater than" (Expression.GT) s’éxercant sur une propriété d’un parametre de l’expression (Expression.Property) et une constante (Expression.Constant).

posted @ 12:51 | Feedback (28)