Expressions Lambda et Méthodes Anonymes
Expressions Lambdas
Introduction
Une expression lambda est une fonction anonyme que vous pouvez utiliser pour créer des délégués ou les types d'arborescence d'expression. À l'aide de expressions lambda, vous pouvez écrire des fonctions locales qui peuvent être passées comme arguments ou être retournées sous forme de valeur des appels de fonction. Les expressions lambda sont particulièrement utiles pour écrire des expressions de requête LINQ.
Pour créer une expression lambda, vous spécifiez des paramètres d'entrée (le cas échéant) situé à gauche de l'opérateur lambda =>, et vous mettez l'expression ou le bloc d'instructions de l'autre côté. Par exemple, l'expression lambda x => x * x spécifie un paramètre nommé x et retourne la valeur d' x un rendu au carré. Vous pouvez assigner cette expression en un type délégué, comme indiqué dans l'exemple suivant :
<code c>delegate int del(int i);
static void Main(string[] args)
{
del myDelegate = x => x * x;
int j = myDelegate(5); //j = 25
}
</code>
Pour créer un type d'arborescence d'expression :
<code>using System.Linq.Expressions;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Expression<del> myET = x => x * x;
}
}
}
</code>
L'opérateur => a la même priorité que l'assignation ( = ) et il est associatif à droite.
Les lambdas sont utilisés dans les requêtes LINQ fondées sur une méthode en tant qu'arguments pour les méthodes d'opérateur de requête standard telles que Where.
Lorsque vous utilisez la syntaxe fondée sur une méthode pour appeler la méthode Where dans la classe Enumerable (de la même manière que dans LINQ to Objects et LINQ to XML) le paramètre est un type délégué System.Func<T, TResult>. Une expression lambda est la méthode la plus commode pour créer ce délégué. Lorsque vous appelez la même méthode dans, par exemple, la classe System.Linq.Queryable (comme vous le faites dans LINQ to SQL), le type de paramètre est un System.Linq.Expressions.Expression<Func> où Func est tout délégué Func comportant seize paramètres d'entrée maximum. Une expression lambda est simplement une méthode très concise pour construire cette arborescence de l'expression. Les lambdas font apparaître les appels Where comme semblables bien qu'en fait, le type d'objet créé à partir du lambda soit différent.
Dans l'exemple précédent, vous remarquez que la signature du délégué comporte un paramètre d'entrée implicitement typé int et retourne un int. L'expression lambda peut être convertie en un délégué de ce type, car elle comporte également un paramètre d'entrée (x) et une valeur de retour que le compilateur peut convertir implicitement en type int. (L'inférence de type est traitée plus en détail dans les sections suivantes.) Lorsque le délégué est appelé à l'aide d'un paramètre d'entrée de 5, il retourne un résultat de 25.
Les lambdas ne sont pas autorisés sur le côté gauche de l'opérateur is ou as.
Toutes les restrictions qui s'appliquent aux méthodes anonymes s'appliquent également aux expressions lambda.
Lambda-Expressions
Une expression lambda est une fonction anonyme utilisée pour créer des délégués ou des arborescences d’expression. Pour créer une expression lambda, si nécessaire on spécifie les paramètres à la gauche de l’opérateur => et l’expression à droite.Ex : x => x * x
Une expression lambda avec une expression sur le côté droit est appelée lambda-expression. Les lambda-expressions sont utilisées en grand nombre dans la construction d'Arborescences d'expression (C# et Visual Basic). Une lambda-expression retourne le résultat de l'expression et prend la forme de base suivante :
(input parameters) => expression
Les parenthèses sont facultatives uniquement si le lambda comporte un paramètre d'entrée ; sinon, elles sont obligatoires. Les paramètres d'entrée sont séparés par des virgules entre parenthèses :
(x, y) => x == y
Il est parfois difficile, voire impossible, pour le compilateur pour déduire les types d'entrée. Dans ce cas, vous pouvez spécifier les types explicitement comme indiqué dans l'exemple suivant :
(int x, string s) => s.Length > x
Spécifiez des paramètres d'entrée de zéro avec des parenthèses vides :
() => SomeMethod()
Notez dans l'exemple précédent que le corps d'une lambda-expression peut se composer d'un appel de méthode. Toutefois, si vous créez des arborescences d'expression qui seront consommées dans un autre domaine, tel que SQL Server, vous ne devez pas utiliser d'appels de méthode dans les expressions lambda. Les méthodes n'auront aucune signification en dehors du contexte du Common Language Runtime .NET.
Lambda-Instructions
Une lambda-instruction ressemble à une lambda-expression, mais l'instruction ou les instructions sont mises entre accolades :
(input parameters) => {statement;}
Le corps d'une lambda-instruction peut se composer d'un nombre illimité d'instructions ; toutefois, en pratique, leur nombre est généralement de deux ou trois.
<code>delegate void TestDelegate(string s);
…
TestDelegate myDel = n => { string s = n + " " + "World"; Console.WriteLine(s); };
myDel("Hello");
</code>
Les lambda-instructions, comme les méthodes anonymes, ne peuvent pas être utilisés pour créer des arborescences d'expression.
Async Lambdas
On peut facilement créer des expressions lambda et les instructions qui incorporent traiter asynchrone à l'aide de les mots clés d' async et d' await . Par exemple, l'exemple suivant Windows Forms contient un gestionnaire d'événements qui appelle et attend une méthode async, ExampleMethodAsync.
<code>
public partial class Form1 : Form{
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
// ExampleMethodAsync returns a Task.
await ExampleMethodAsync();
textBox1.Text += "\r\nControl returned to Click event handler.\r\n";
}
async Task ExampleMethodAsync()
{
// The following line simulates a task-returning asynchronous process.
await Task.Delay(1000);
}
}
</code>
Vous pouvez ajouter le même gestionnaire d'événements à l'aide d'un async lambda. Pour ajouter le gestionnaire, ajoutez un modificateur d' async avant la liste de paramètres lambda, comme le montre l'exemple suivant.
<code>
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button1.Click += async (sender, e) =>
{
// ExampleMethodAsync returns a Task.
await ExampleMethodAsync();
textBox1.Text += "\r\nControl returned to Click event handler.\r\n";
};
}
async Task ExampleMethodAsync()
{
// The following line simulates a task-returning asynchronous process.
await Task.Delay(1000);
}
}
</code>
==== Lambdas avec les opérateurs de requête standard ====
De nombreux opérateurs de requête standard comportent un paramètre d'entrée dont le type est l'un de la famille Func<T, TResult> de délégués génériques. Les délégués Func<T, TResult> utilisent des paramètres de type pour définir le nombre et le type des paramètres d'entrée ainsi que le type de retour du délégué. Les délégués Func sont très utiles pour l'encapsulation des expressions définies par l'utilisateur appliquées à chaque élément dans un jeu de données sources. Considérons par exemple le type délégué suivant :
public delegate TResult Func<TArg0, TResult>(TArg0 arg0)
Ce délégué peut être instancié comme Func<int,bool> myFunc où int est un paramètre d'entrée et bool la valeur de retour. La valeur de retour est toujours spécifiée dans le dernier paramètre de type. Func<int, string, bool> définit un délégué avec deux paramètres d'entrée, int et string et un type de retour de bool. Le délégué Func suivant, lorsqu'il est appelé, retourne la valeur true ou false pour indiquer si le paramètre d'entrée est égal à 5 :
Func<int, bool> myFunc = x => x == 5;
bool result = myFunc(4); // returns false of course
Vous pouvez également fournir une expression lambda lorsque le type d'argument est Expression<Func>, par exemple dans les opérateurs de requête standard définis dans System.Linq.Queryable. Lorsque vous spécifiez un argument Expression<Func>, le lambda est compilé en une arborescence d'expression.
Un opérateur de requête standard, la méthode Count, est illustré ici :
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(n => n % 2 == 1);
Le compilateur peut déduire le type du paramètre d'entrée, ou vous pouvez également le spécifier explicitement. Cette expression lambda particulière compte les entiers (n) qui, lorsqu'ils sont divisés par deux, ont un reste de 1.
La méthode suivante génère une séquence qui contient tous les éléments du tableau d' numbers qui sont à gauche des 9 car il s'agit du premier nombre dans la séquence qui ne remplit pas la condition :
var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);
Cet exemple indique comment spécifier plusieurs paramètres d'entrée en les mettant entre parenthèses. La méthode retourne tous les éléments du tableau de nombres, jusqu'à ce que soit rencontré un nombre dont la valeur est inférieure à cette position. Ne confondez pas l'opérateur lambda (=>) avec l'opérateur supérieur ou égal (>=).
var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);
Variable Scope in Lambda Expressions
Lambdas can refer to outer variables that are in scope in the enclosing method or type in which the lambda is defined. Variables that are captured in this manner are stored for use in the lambda expression even if variables would otherwise go out of scope and be garbage collected. An outer variable must be definitely assigned before it can be consumed in a lambda expression. The following example demonstrates these rules:
<code>
delegate bool D();delegate bool D2(int i);
class Test
{
D del;
D2 del2;
public void TestMethod(int input)
{
int j = 0;
// Initialize the delegates with lambda expressions.
// Note access to 2 outer variables.
// del will be invoked within this method.
del = () => { j = 10; return j > input; };
// del2 will be invoked after TestMethod goes out of scope.
del2 = (x) => {return x == j; };
// Demonstrate value of j:
// Output: j = 0
// The delegate has not been invoked yet.
Console.WriteLine("j = {0}", j); // Invoke the delegate.
bool boolResult = del();
// Output: j = 10 b = True
Console.WriteLine("j = {0}. b = {1}", j, boolResult);
}
static void Main()
{
Test test = new Test();
test.TestMethod(5);
// Prove that del2 still has a copy of
// local variable j from TestMethod.
bool result = test.del2(10);
// Output: True
Console.WriteLine(result);
Console.ReadKey();
}
}
</code>
The following rules apply to variable scope in lambda expressions:
A variable that is captured will not be garbage-collected until the delegate that references it goes out of scope.
Variables introduced within a lambda expression are not visible in the outer method.
A lambda expression cannot directly capture a ref or out parameter from an enclosing method.
A return statement in a lambda expression does not cause the enclosing method to return.
A lambda expression cannot contain a goto statement, break statement, or continue statement whose target is outside the body or in the body of a contained anonymous function.
Méthodes Anonymes
Une méthode anonyme est essentiellement un bloc de code passé en en paramètre à un délégué.
Avant C# 2.0, la seule façon de déclarer un délégué consistait à utiliser des méthodes nommées. C# 2.0 a introduit les méthodes anonymes et dans C# 3.0 et les versions ultérieures, les expressions lambda remplacent des méthodes anonymes comme manière privilégiée d'écrire du code incorporé.
Il existe un cas dans lequel une méthode anonyme fournit des fonctionnalités introuvables dans les expressions lambda : Les méthodes anonymes vous permettent d'omettre la liste de paramètres. Cela signifie qu'une méthode anonyme peut être convertie en délégués avec diverses signatures. Ceci est impossible avec les expressions lambda.
Button1.Click += delegate(System.Object o, System.EventArgs e)
{ System.Windows.Forms.MessageBox.Show("Click!"); };
Dans les versions de C# antérieures à 2.0, la seule façon de déclarer un délégué consistait à utiliser des méthodes nommées. C# 2.0 a introduit les méthodes anonymes et dans C# 3.0 et les versions ultérieures, les expressions lambda remplacent des méthodes anonymes comme manière privilégiée d'écrire du code incorporé. Toutefois, les informations à propos des méthodes anonymes dans cette rubrique s'appliquent également aux expressions lambda. Il existe un cas dans lequel une méthode anonyme fournit des fonctionnalités introuvables dans les expressions lambda. Les méthodes anonymes vous permettent d'omettre la liste de paramètres. Cela signifie qu'une méthode anonyme peut être convertie en délégués avec diverses signatures. Ceci est impossible avec les Expressions Lambda.
La création de méthodes anonymes est essentiellement un moyen de passer un bloc de code en paramètre de délégué. En voici deux exemples :
<code>// Create a handler for a click event.
button1.Click += delegate(System.Object o, System.EventArgs e) {System.Windows.Forms.MessageBox.Show("Click!"); };
</code>
<code>
// Create a delegate.
delegate void Del(int x);
// Instantiate the delegate using an anonymous method.
Del d = delegate(int k) { /* ... */ };
</code>
En utilisant des méthodes anonymes, vous réduisez la charge de travail liée au codage de l'instanciation de délégués, car vous n'avez pas à créer de méthode distincte.
Par exemple, la spécification d'un bloc de code au lieu d'un délégué peut être utile dans une situation où le travail nécessité par la création d'une méthode ne semble pas justifié. Un bon exemple pourrait être le moment où vous démarrez un nouveau thread. Cette classe crée un thread et contient également le code exécuté par le thread sans créer une méthode supplémentaire pour le délégué.
<code>void StartThread()
{
System.Threading.Thread t1 = new System.Threading.Thread
(delegate()
{
System.Console.Write("Hello, ");
System.Console.WriteLine("World!");
});
t1.Start();
}
</code>
Arborescences d’Expression
Un arbre d’expression est une requête structurée utilisée pour interroger des sources de données implémentant IQueryable<T>
Les arborescences d’expression sont notamment utilisées par LINQ pour représenter les expressions Lambda.
Commentaires
Enregistrer un commentaire