Expressions lambda (Guide de programmation C#)Lambda Expressions (C# Programming Guide)

Une expression lambda est une fonction anonyme que vous pouvez utiliser pour créer des types délégués ou des types d' arborescence d'expression .A lambda expression is an anonymous function that you can use to create delegates or expression tree types. En utilisant des expressions lambda, vous pouvez écrire des fonctions locales qui peuvent être passées comme des arguments ou retournées comme la valeur d'appels de fonction.By using lambda expressions, you can write local functions that can be passed as arguments or returned as the value of function calls. Les expressions lambda sont particulièrement utiles pour écrire des expressions de requête LINQ.Lambda expressions are particularly helpful for writing LINQ query expressions.

Pour créer une expression lambda, vous spécifiez des paramètres d'entrée (le cas échéant) à gauche de l'opérateur lambda =>, et vous placez l'expression ou le bloc d'instructions de l'autre côté.To create a lambda expression, you specify input parameters (if any) on the left side of the lambda operator =>, and you put the expression or statement block on the other side. Par exemple, l'expression lambda x => x * x spécifie un paramètre nommé x et retourne la valeur x élevée au carré.For example, the lambda expression x => x * x specifies a parameter that’s named x and returns the value of x squared. Vous pouvez assigner cette expression à un type délégué, comme dans l'exemple suivant :You can assign this expression to a delegate type, as the following example shows:

delegate int del(int i);  
static void Main(string[] args)  
{  
    del myDelegate = x => x * x;  
    int j = myDelegate(5); //j = 25  
}  

Pour créer un type d'arborescence d'expression :To create an expression tree type:

using System.Linq.Expressions;  
  
namespace ConsoleApplication1  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            Expression<del> myET = x => x * x;  
        }  
    }  
}  

L'opérateur => a la même priorité que l'assignation (=) et est de type right associative (consultez la section « Associativité » de l'article Opérateurs).The => operator has the same precedence as assignment (=) and is right associative (see "Associativity" section of the Operators article).

Les lambdas sont utilisés dans les requêtes LINQLINQ fondées sur une méthode en tant qu'arguments pour les méthodes d'opérateur de requête standard telles que Where.Lambdas are used in method-based LINQLINQ queries as arguments to standard query operator methods such as 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 LINQLINQ to Objects et LINQ to XMLLINQ to XML), le paramètre est un type délégué System.Func<T,TResult>.When you use method-based syntax to call the Where method in the Enumerable class (as you do in LINQLINQ to Objects and LINQ to XMLLINQ to XML) the parameter is a delegate type System.Func<T,TResult>. Une expression lambda est la méthode la plus commode pour créer ce délégué.A lambda expression is the most convenient way to create that delegate. Quand vous appelez la même méthode dans, par exemple, la classe System.Linq.Queryable (comme vous le faites dans LINQ to SQLLINQ to SQL), le type de paramètre est System.Linq.Expressions.Expression<Func>, où Func correspond à un délégué Func avec seize paramètres d'entrée maximum.When you call the same method in, for example, the System.Linq.Queryable class (as you do in LINQ to SQLLINQ to SQL) then the parameter type is an System.Linq.Expressions.Expression<Func> where Func is any of the Func delegates with up to sixteen input parameters. Une expression lambda est simplement une méthode très concise pour construire cette arborescence de l'expression.Again, a lambda expression is just a very concise way to construct that expression tree. 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.The lambdas allow the Where calls to look similar although in fact the type of object created from the lambda is different.

Dans l'exemple précédent, vous remarquez que la signature du délégué comporte un paramètre d'entrée implicitement typé intet retourne un int.In the previous example, notice that the delegate signature has one implicitly-typed input parameter of type int, and returns an 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.The lambda expression can be converted to a delegate of that type because it also has one input parameter (x) and a return value that the compiler can implicitly convert to 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.(Type inference is discussed in more detail in the following sections.) When the delegate is invoked by using an input parameter of 5, it returns a result of 25.

Les expressions lambda ne sont pas autorisées du côté gauche de l’opérateur is ou as.Lambdas are not allowed on the left side of the is or as operator.

Toutes les restrictions qui s'appliquent aux méthodes anonymes s'appliquent également aux expressions lambda.All restrictions that apply to anonymous methods also apply to lambda expressions. Pour plus d’informations, consultez Méthodes anonymes.For more information, see Anonymous Methods.

Expressions lambdaExpression Lambdas

Une expression lambda avec une expression du côté droit de l’opérateur => est appelée expression lambda.A lambda expression with an expression on the right side of the => operator is called an expression lambda. Les expressions lambda sont utilisées en grand nombre dans la construction d’arborescences d’expression.Expression lambdas are used extensively in the construction of Expression Trees. Une expression lambda retourne le résultat de l'expression et prend la forme de base suivante :An expression lambda returns the result of the expression and takes the following basic form:

(input-parameters) => expression

Les parenthèses sont facultatives uniquement si le lambda comporte un paramètre d'entrée ; sinon, elles sont obligatoires.The parentheses are optional only if the lambda has one input parameter; otherwise they are required. Les paramètres d'entrée sont séparés par des virgules entre parenthèses :Two or more input parameters are separated by commas enclosed in parentheses:

(x, y) => x == y

Il est parfois difficile, voire impossible, pour le compilateur de déduire les types d'entrée.Sometimes it is difficult or impossible for the compiler to infer the input types. Dans ce cas, vous pouvez spécifier les types explicitement comme indiqué dans l'exemple suivant :When this occurs, you can specify the types explicitly as shown in the following example:

(int x, string s) => s.Length > x

Les types de paramètre d’entrée doivent être tous explicites ou implicites. Sinon, C# génère l’erreur de compilateur CS0748.Input parameter types must be all explicit or all implicit; otherwise, C# generates a CS0748 compiler error.

Spécifiez des paramètres d'entrée de zéro avec des parenthèses vides :Specify zero input parameters with empty parentheses:

() => SomeMethod()

Notez dans l'exemple précédent que le corps d'une expression lambda peut se composer d'un appel de méthode.Note in the previous example that the body of an expression lambda can consist of a method call. Toutefois, si vous créez des arborescences d'expressions évaluées en dehors de .NET Framework, comme dans SQL Server, vous ne devez pas utiliser les appels de méthode dans les expressions lambda.However, if you are creating expression trees that are evaluated outside of the .NET Framework, such as in SQL Server, you should not use method calls in lambda expressions. Les méthodes n'auront aucune signification en dehors du contexte du Common Language Runtime .NET.The methods will have no meaning outside the context of the .NET common language runtime.

Instructions lambdaStatement Lambdas

Une instruction lambda ressemble à une expression lambda, mais l'instruction ou les instructions sont mises entre accolades :A statement lambda resembles an expression lambda except that the statement(s) is enclosed in braces:

(paramètres d’entrée) => { instruction ; }(input-parameters) => { statement; }

Le corps d'une instruction lambda peut se composer d'un nombre illimité d'instructions ; toutefois, en pratique, leur nombre est généralement de deux ou trois.The body of a statement lambda can consist of any number of statements; however, in practice there are typically no more than two or three.

[!code-csharp[StatementLamba#1](../../../../samples\snippets\csharp\programming-guide\lambda-expressions/statements.cs#1)][!code-csharp[StatementLamba#2](../../../../samples\snippets\csharp\programming-guide\lambda-expressions/statements.cs#2)]

Les instructions lambda, comme les méthodes anonymes, ne peuvent pas être utilisées pour créer des arborescences d'expression.Statement lambdas, like anonymous methods, cannot be used to create expression trees.

Lambdas asynchronesAsync Lambdas

Vous pouvez facilement créer des expressions et des instructions lambda qui incorporent le traitement asynchrone à l'aide des mots clés async et await .You can easily create lambda expressions and statements that incorporate asynchronous processing by using the async and await keywords. Par exemple, l'exemple Windows Forms suivant contient un gestionnaire d'événements qui appelle et attend une méthode async ExampleMethodAsync.For example, the following Windows Forms example contains an event handler that calls and awaits an async method, ExampleMethodAsync.

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.\n";  
    }  
  
    async Task ExampleMethodAsync()  
    {  
        // The following line simulates a task-returning asynchronous process.  
        await Task.Delay(1000);  
    }  
}  

Vous pouvez ajouter le même gestionnaire d'événements en utilisant une expression lambda asynchrone.You can add the same event handler by using an async lambda. Pour ajouter ce gestionnaire, ajoutez un modificateur async avant la liste des paramètres lambda, comme le montre l'exemple suivant.To add this handler, add an async modifier before the lambda parameter list, as the following example shows.

public partial class Form1 : Form  
{  
    public Form1()  
    {  
        InitializeComponent();  
        button1.Click += async (sender, e) =>  
        {  
            // ExampleMethodAsync returns a Task.  
            await ExampleMethodAsync();  
            textBox1.Text += "\nControl returned to Click event handler.\n";  
        };  
    }  
  
    async Task ExampleMethodAsync()  
    {  
        // The following line simulates a task-returning asynchronous process.  
        await Task.Delay(1000);  
    }  
}  

Pour plus d’informations sur la création et l’utilisation des méthodes asyncrones, consultez Programmation asynchrone avec async et await.For more information about how to create and use async methods, see Asynchronous Programming with async and await.

Lambdas avec les opérateurs de requête standardLambdas with the Standard Query Operators

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.Many Standard query operators have an input parameter whose type is one of the Func<T,TResult> family of generic delegates. Ces délégués utilisent des paramètres de type pour définir le nombre et les types de paramètres d'entrée, ainsi que le type de retour du délégué.These delegates use type parameters to define the number and types of input parameters, and the return type of the delegate. Les déléguésFunc 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.Func delegates are very useful for encapsulating user-defined expressions that are applied to each element in a set of source data. Considérons par exemple le type délégué suivant :For example, consider the following delegate type:

public delegate TResult Func<TArg0, TResult>(TArg0 arg0)  

Ce délégué peut être instancié comme Func<int,bool> myFuncint est un paramètre d'entrée et bool est la valeur de retour.The delegate can be instantiated as Func<int,bool> myFunc where int is an input parameter and bool is the return value. La valeur de retour est toujours spécifiée dans le dernier paramètre de type.The return value is always specified in the last type parameter. Func<int, string, bool> définit un délégué avec deux paramètres d'entrée, int et stringet un type de retour de bool.Func<int, string, bool> defines a delegate with two input parameters, int and string, and a return type of 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 :The following Func delegate, when it is invoked, will return true or false to indicate whether the input parameter is equal to 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.You can also supply a lambda expression when the argument type is an Expression<Func>, for example in the standard query operators that are defined in System.Linq.Queryable. Lorsque vous spécifiez un argument Expression<Func> , le lambda est compilé en une arborescence d'expression.When you specify an Expression<Func> argument, the lambda will be compiled to an expression tree.

Opérateur de requête standard, la méthode Count , est illustré ici :A standard query operator, the Count method, is shown here:

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.The compiler can infer the type of the input parameter, or you can also specify it explicitly. Cette expression lambda particulière compte les entiers (n) qui, lorsqu'ils sont divisés par deux, ont un reste de 1.This particular lambda expression counts those integers (n) which when divided by two have a remainder of 1.

La ligne de code suivante produit une séquence qui contient tous les éléments du tableau numbers qui apparaissent à gauche du « 9 », car c'est le premier nombre de la séquence qui ne satisfait pas la condition :The following line of code produces a sequence that contains all elements in the numbers array that are to the left side of the 9 because that's the first number in the sequence that doesn't meet the 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.This example shows how to specify multiple input parameters by enclosing them in parentheses. 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.The method returns all the elements in the numbers array until a number is encountered whose value is less than its position. Ne confondez pas l'opérateur lambda (=>) avec l'opérateur supérieur ou égal (>=).Do not confuse the lambda operator (=>) with the greater than or equal operator (>=).

var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);  

Inférence de type dans les lambdasType Inference in Lambdas

Lorsque vous écrivez des lambdas, vous n'avez généralement pas à spécifier un type pour les paramètres d'entrée, car le compilateur peut déduire le type en fonction du corps du lambda, du type délégué du paramètre et d'autres facteurs décrits dans la spécification du langage C#.When writing lambdas, you often do not have to specify a type for the input parameters because the compiler can infer the type based on the lambda body, the parameter’s delegate type, and other factors as described in the C# Language Specification. Pour la plupart des opérateurs de requête standard, la première entrée est le type des éléments dans la séquence source.For most of the standard query operators, the first input is the type of the elements in the source sequence. Donc, si vous interrogez un IEnumerable<Customer>, il en est déduit que la variable d'entrée est un objet Customer , ce qui signifie que vous avez accès à ses méthodes et propriétés :So if you are querying an IEnumerable<Customer>, then the input variable is inferred to be a Customer object, which means you have access to its methods and properties:

customers.Where(c => c.City == "London");  

Les règles générales pour les lambdas sont les suivantes :The general rules for lambdas are as follows:

  • Le lambda doit contenir le même nombre de paramètres que le type délégué.The lambda must contain the same number of parameters as the delegate type.

  • Chaque paramètre d'entrée dans le lambda doit être implicitement convertible en son paramètre de délégué correspondant.Each input parameter in the lambda must be implicitly convertible to its corresponding delegate parameter.

  • La valeur de retour du lambda (le cas échéant) doit être implicitement convertible en type de retour du délégué.The return value of the lambda (if any) must be implicitly convertible to the delegate's return type.

Notez que les expressions lambda en elles-mêmes n'ont pas de type, car le système de type commun (CTS, Common Type System) ne comporte aucun concept intrinsèque « d'expression lambda ».Note that lambda expressions in themselves do not have a type because the common type system has no intrinsic concept of "lambda expression." Toutefois, il est parfois commode de parler de manière informelle du « type » d'une expression lambda.However, it is sometimes convenient to speak informally of the "type" of a lambda expression. Dans ce cas, le type fait référence au type délégué ou au type Expression dans lequel est convertie l'expression lambda.In these cases the type refers to the delegate type or Expression type to which the lambda expression is converted.

Portée variable dans les expressions lambdaVariable Scope in Lambda Expressions

Les expressions lambda peuvent faire référence à des variables externes (consultez Méthodes anonymes) qui se trouvent dans l’étendue de la méthode qui définit la fonction lambda, ou dans l’étendue du type qui contient l’expression lambda.Lambdas can refer to outer variables (see Anonymous Methods) that are in scope in the method that defines the lambda function, or in scope in the type that contains the lambda expression. Les variables capturées de cette manière sont stockées pour une utilisation dans l'expression lambda, même si les variables se trouvent en dehors de la portée et sont récupérées par le garbage collector.Variables that are captured in this manner are stored for use in the lambda expression even if the variables would otherwise go out of scope and be garbage collected. Une variable externe doit être assignée de manière précise pour pouvoir être utilisée dans une expression lambda.An outer variable must be definitely assigned before it can be consumed in a lambda expression. L'exemple suivant illustre ces règles :The following example demonstrates these rules:

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();  
    }  
}  

Les règles suivantes s'appliquent à la portée des variables dans les expressions lambda :The following rules apply to variable scope in lambda expressions:

  • Une variable qui est capturée ne sera pas récupérée par le garbage collector avant que le délégué qui le référence soit disponible pour le garbage collection.A variable that is captured will not be garbage-collected until the delegate that references it becomes eligible for garbage collection.

  • Les variables introduites dans une expression lambda ne sont pas visibles dans la méthode externe.Variables introduced within a lambda expression are not visible in the outer method.

  • Une expression lambda ne peut pas capturer directement un paramètre in, ref ou out dans une méthode englobante.A lambda expression cannot directly capture an in, ref, or out parameter from an enclosing method.

  • Une instruction return dans une expression lambda ne provoque pas le retour de la méthode englobante.A return statement in a lambda expression does not cause the enclosing method to return.

  • Une expression lambda ne peut pas contenir une instruction goto , une instruction break , ou une instruction continue à l'intérieur de la fonction lambda si la cible de l'instruction de saut est hors du bloc.A lambda expression cannot contain a goto statement, break statement, or continue statement that is inside the lambda function if the jump statement’s target is outside the block. C'est également une erreur d'avoir une instruction de saut en dehors du bloc de la fonction lambda si la cible est à l'intérieur du bloc.It is also an error to have a jump statement outside the lambda function block if the target is inside the block.

Spécification du langage C#C# Language Specification

Pour plus d'informations, voir la spécification du langage C#.For more information, see the C# Language Specification. La spécification du langage est la source de référence pour la syntaxe C# et son utilisation.The language specification is the definitive source for C# syntax and usage.

Delegates, Events, and Lambda Expressions (Délégués, événements et expressions lambda) dans C# 3.0 Cookbook, Third Edition: More than 250 solutions for C# 3.0 programmersDelegates, Events, and Lambda Expressions in C# 3.0 Cookbook, Third Edition: More than 250 solutions for C# 3.0 programmers

Voir aussiSee Also