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

Une expression lambda est un bloc de code (une expression ou un bloc d’instructions) qui est traité comme un objet.A lambda expression is a block of code (an expression or a statement block) that is treated as an object. Elle peut être passée comme argument à des méthodes, et peut aussi être retournée par des appels de méthode.It can be passed as an argument to methods, and it can also be returned by method calls. Les expressions lambda sont largement utilisées pour :Lambda expressions are used extensively for:

Les expressions lambda sont du code qui peut être représenté comme un délégué, ou comme une arborescence d’expressions qui est compilée en délégué.Lambda expressions are code that can be represented either as a delegate, or as an expression tree that compiles to a delegate. Le type délégué spécifique d’une expression lambda dépend de ses paramètres et de sa valeur de retour.The specific delegate type of a lambda expression depends on its parameters and return value. Les expressions lambda qui ne retournent pas de valeur correspondent à un délégué Action spécifique, en fonction du nombre de ses paramètres.Lambda expressions that don't return a value correspond to a specific Action delegate, depending on its number of parameters. Les expressions lambda qui retournent une valeur correspondent à un délégué Func spécifique, en fonction du nombre de ses paramètres.Lambda expressions that return a value correspond to a specific Func delegate, depending on its number of parameters. Par exemple, une expression lambda qui a deux paramètres, mais qui ne retourne aucune valeur correspond à un délégué Action<T1,T2>.For example, a lambda expression that has two parameters but returns no value corresponds to an Action<T1,T2> delegate. Une expression lambda qui a un paramètre et qui retourne une valeur correspond à un délégué Func<T,TResult>.A lambda expression that has one parameter and returns a value corresponds to Func<T,TResult> delegate.

Une expression lambda utilise =>, l’opérateur de déclaration lambda, pour séparer la liste des paramètres de l’expression lambda de son code exécutable.A lambda expression uses =>, the lambda declaration operator, to separate the lambda's parameter list from its executable code. Pour créer une expression lambda, vous spécifiez des paramètres d’entrée (le cas échéant) du côté 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 d’une seule ligne x => x * x spécifie un paramètre nommé x et retourne la valeur x élevée au carré.For example, the single-line 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:

Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
// Output:
// 25

Il est également possible d’affecter une expression lambda à un type d’arborescence d’expression :You also can assign a lambda expression to an expression tree type:

System.Linq.Expressions.Expression<Func<int, int>> e = x => x * x;
Console.WriteLine(e);
// Output:
// x => (x * x)

Vous pouvez aussi le passer directement comme argument de méthode :Or you can pass it directly as a method argument:

int[] numbers = { 2, 3, 4, 5 };
var squaredNumbers = numbers.Select(x => x * x);
Console.WriteLine(string.Join(" ", squaredNumbers));
// Output:
// 4 9 16 25

Lorsque la méthode Enumerable.Select est appelée avec une syntaxe fondée sur une méthode dans la classe System.Linq.Enumerable (comme dans LINQ to Objects et LINQ to XML), le paramètre est un type délégué System.Func<T,TResult>.When you use method-based syntax to call the Enumerable.Select method in the System.Linq.Enumerable class (as you do in LINQ to Objects and LINQ 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. Si la méthode Queryable.Select est appelée dans la classe System.Linq.Queryable (comme dans LINQ to SQL), le paramètre est un type d’arborescence d’expression Expression<Func<TSource,TResult>>.When you call the Queryable.Select method in the System.Linq.Queryable class (as you do in LINQ to SQL) the parameter type is an expression tree type Expression<Func<TSource,TResult>>. 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 Select comme semblables bien qu'en fait, le type d'objet créé à partir du lambda soit différent.The lambdas allow the Select calls to look similar although in fact the type of object created from the lambda is different.

Toutes les restrictions qui s’appliquent aux méthodes anonymes valent également pour les expressions lambda.All restrictions that apply to anonymous methods also apply to lambda expressions.

Expressions lambdasExpression lambdas

Une expression lambda comportant une expression à droite 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’expressions.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.

Utilisez des parenthèses vides s'il n'y a aucun paramètre d'entrée :Specify zero input parameters with empty parentheses:

Action line = () => Console.WriteLine();

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:

Func<int, int, bool> testForEquality = (x, y) => x == y;

Il est parfois impossible pour le compilateur de déduire les types d’entrée.Sometimes it's impossible for the compiler to infer the input types. Vous pouvez spécifier les types explicitement comme dans l’exemple suivant :You can specify the types explicitly as shown in the following example:

Func<int, string, bool> isTooLong = (int x, string s) => s.Length > x;

Les types de paramètres d’entrée doivent être tous explicites ou tous implicites ; sinon, une erreur de compilateur CS0748 se produit.Input parameter types must be all explicit or all implicit; otherwise, a CS0748 compiler error occurs.

Le corps d’une expression lambda peut se composer d’un appel de méthode.The body of an expression lambda can consist of a method call. Cependant, si vous créez des arborescences d’expressions évaluées en dehors du contexte du common language runtime .NET, comme dans SQL Server, vous ne devez pas utiliser d’appels de méthode dans les expressions lambda.However, if you are creating expression trees that are evaluated outside the context of the .NET common language runtime, 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:

(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.

Action<string> greet = name => 
{ 
    string greeting = $"Hello {name}!";
    Console.WriteLine(greeting);
};
greet("World");
// Output:
// Hello World!

Les instructions lambda, comme les méthodes anonymes, ne peuvent pas être utilisées pour créer des arborescences d'expressions.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();
        button1.Click += button1_Click;
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        await ExampleMethodAsync();
        textBox1.Text += "\r\nControl returned to Click event handler.\n";
    }

    private 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 dans 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) =>
        {
            await ExampleMethodAsync();
            textBox1.Text += "\r\nControl returned to Click event handler.\n";
        };
    }

    private 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.

Expressions lambda et tuplesLambda expressions and tuples

À compter de sa version 7.0, le langage C# offre une prise en charge intégrée des tuples.Starting with C# 7.0, the C# language provides built-in support for tuples. Vous pouvez fournir un tuple comme argument à une expression lambda, et votre expression lambda peut aussi retourner un tuple.You can provide a tuple as an argument to a lambda expression, and your lambda expression can also return a tuple. Dans certains cas, le compilateur C# utilise l’inférence de type pour déterminer les types des composants du tuple.In some cases, the C# compiler uses type inference to determine the types of tuple components.

Vous définissez un tuple en plaçant entre des parenthèses une liste de ses composants avec des virgules comme séparateur.You define a tuple by enclosing a comma-delimited list of its components in parentheses. L’exemple suivant utilise un tuple à trois composants pour passer une séquence de nombres à une expression lambda, qui double chaque valeur et retourne un tuple à trois composants contenant le résultat des multiplications.The following example uses tuple with three components to pass a sequence of numbers to a lambda expression, which doubles each value and returns a tuple with three components that contains the result of the multiplications.

Func<(int, int, int), (int, int, int)> doubleThem = ns => (2 * ns.Item1, 2 * ns.Item2, 2 * ns.Item3);
var numbers = (2, 3, 4);
var doubledNumbers = doubleThem(numbers);
Console.WriteLine($"The set {numbers} doubled: {doubledNumbers}");
// Output:
// The set (2, 3, 4) doubled: (4, 6, 8)

En général, les champs d’un tuple sont nommés Item1, Item2, etc. Vous pouvez cependant définir un tuple avec des composants nommés, comme dans l’exemple suivant.Ordinarily, the fields of a tuple are named Item1, Item2, etc. You can, however, define a tuple with named components, as the following example does.

Func<(int n1, int n2, int n3), (int, int, int)> doubleThem = ns => (2 * ns.n1, 2 * ns.n2, 2 * ns.n3);
var numbers = (2, 3, 4);
var doubledNumbers = doubleThem(numbers);
Console.WriteLine($"The set {numbers} doubled: {doubledNumbers}");

Pour plus d’informations sur les tuples C#, voir Types tuples C#.For more information about C# tuples, see C# tuple types.

Lambdas avec les opérateurs de requête standardLambdas with the standard query operators

LINQ to Objects, parmi d’autres implémentations, a un paramètre d’entrée dont le type fait partie de la famille Func<TResult> de délégués génériques.LINQ to Objects, among other implementations, have an input parameter whose type is one of the Func<TResult> family of generic delegates. Ces délégués 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é.These delegates use type parameters to define the number and type 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. Prenons par exemple le type délégué Func<T,TResult> :For example, consider the Func<T,TResult> delegate type:

public delegate TResult Func<in T, out TResult>(T arg)

Ce délégué peut être instancié comme une instance Func<int, bool>, où int est un paramètre d’entrée et bool la valeur de retour.The delegate can be instantiated as a Func<int, bool> instance 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. Par exemple, 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 bool.For example, 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 une valeur booléenne indiquant si le paramètre d’entrée est égal à cinq :The following Func delegate, when it's invoked, returns Boolean value that indicates whether the input parameter is equal to five:

Func<int, bool> equalsFive = x => x == 5;
bool result = equalsFive(4);
Console.WriteLine(result);   // False

Vous pouvez aussi fournir une expression lambda quand le type d’argument est Expression<TDelegate>, par exemple, dans les opérateurs de requête standard définis dans le type Queryable.You can also supply a lambda expression when the argument type is an Expression<TDelegate>, for example in the standard query operators that are defined in the Queryable type. Quand vous spécifiez un argument Expression<TDelegate>, l’expression lambda est compilée en arborescence de l’expression.When you specify an Expression<TDelegate> argument, the lambda is compiled to an expression tree.

L’exemple suivant utilise l’opérateur de requête standard Count :The following example uses the Count standard query operator:

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(n => n % 2 == 1);
Console.WriteLine($"There are {oddNumbers} odd numbers in {string.Join(" ", numbers)}");

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.

L’exemple suivant produit une séquence qui contient tous les éléments du tableau numbers précédant le 9, car c’est le premier nombre de la séquence qui ne remplit pas la condition :The following example produces a sequence that contains all elements in the numbers array that precede the 9, because that's the first number in the sequence that doesn't meet the condition:

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var firstNumbersLessThanSix = numbers.TakeWhile(n => n < 6);
Console.WriteLine(string.Join(" ", firstNumbersLessThanSix));
// Output:
// 5 4 1 3

L’exemple suivant spécifie plusieurs paramètres d’entrée en les plaçant entre parenthèses.The following example specifies multiple input parameters by enclosing them in parentheses. La méthode retourne tous les éléments du tableau numbers jusqu’à ce qu’elle rencontre un nombre dont la valeur est inférieure à sa position ordinale dans le tableau :The method returns all the elements in the numbers array until it encounters a number whose value is less than its ordinal position in the array:

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);
Console.WriteLine(string.Join(" ", firstSmallNumbers));
// Output:
// 5 4

Inférence de type et expressions lambdaType inference in lambda expressions

Il n’est généralement pas nécessaire, avec les expressions lambda, de spécifier un type pour les paramètres d’entrée, car le compilateur peut le déduire du corps de l’expression, des types de paramètres et d’autres facteurs, comme le décrit la spécification du langage C#.When writing lambdas, you often don't have to specify a type for the input parameters because the compiler can infer the type based on the lambda body, the parameter types, 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. Si vous interrogez un IEnumerable<Customer>, le type de la variable d’entrée est inféré comme étant un objet Customer, ce qui signifie que vous avez accès à ses méthodes et à ses propriétés :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");

Voici les règles générales de l’inférence de type pour les expressions lambda :The general rules for type inference 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 n’ont pas de type en elles-mêmes, 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 don't 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's 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.

Étendue variable dans les expressions lambdaVariable scope in lambda expressions

Les expressions lambda peuvent faire référence à des variables externes (voir Méthodes anonymes) dans l’étendue de la méthode qui définit l’expression 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 expression, 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:

public static class VariableScopeWithLambdas
{
    public class VariableCaptureGame
    {
        internal Action<int> updateCapturedLocalVariable;
        internal Func<int, bool> isEqualToCapturedLocalVariable;

        public void Run(int input)
        {
            int j = 0;

            updateCapturedLocalVariable = x =>
            {
                j = x;
                bool result = j > input;
                Console.WriteLine($"{j} is greater than {input}: {result}");
            };

            isEqualToCapturedLocalVariable = x => x == j;

            Console.WriteLine($"Local variable before lambda invocation: {j}");
            updateCapturedLocalVariable(10);
            Console.WriteLine($"Local variable after lambda invocation: {j}");
        }
    }

    public static void Main()
    {  
        var game = new VariableCaptureGame();
        
        int gameInput = 5;
        game.Run(gameInput);

        int jTry = 10;
        bool result = game.isEqualToCapturedLocalVariable(jTry);
        Console.WriteLine($"Captured local variable is equal to {jTry}: {result}");

        int anotherJ = 3;
        game.updateCapturedLocalVariable(anotherJ);

        bool equalToAnother = game.isEqualToCapturedLocalVariable(anotherJ);
        Console.WriteLine($"Another lambda observes a new value of captured variable: {equalToAnother}");
    }
    // Output:
    // Local variable before lambda invocation: 0
    // 10 is greater than 5: True
    // Local variable after lambda invocation: 10
    // Captured local variable is equal to 10: True
    // 3 is greater than 5: False
    // Another lambda observes a new value of captured variable: True
}

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 englobante.Variables introduced within a lambda expression are not visible in the enclosing method.

  • Une expression lambda ne peut pas capturer directement un paramètre in, ref ou out dans la méthode englobante.A lambda expression cannot directly capture an in, ref, or out parameter from the 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 doesn't cause the enclosing method to return.

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

spécification du langage C#C# language specification

Pour plus d’informations, consultez la section Expressions de fonction anonyme de la spécification du langage C#.For more information, see the Anonymous function expressions section of the C# language specification.

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