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

Une expression lambda est une expression de l’une des deux formes suivantes :A lambda expression is an expression of any of the following two forms:

Utilisez l’opérateur de déclaration lambda=> pour séparer la liste des paramètres de l’expression lambda de son corps.Use the lambda declaration operator => to separate the lambda's parameter list from its body. 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 une expression ou un 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 an expression or a statement block on the other side.

Toute expression lambda peut être convertie en type délégué.Any lambda expression can be converted to a delegate type. Le type délégué vers lequel une expression lambda peut être convertie est défini par les types de ses paramètres et de sa valeur de retour.The delegate type to which a lambda expression can be converted is defined by the types of its parameters and return value. Si une expression lambda ne retourne pas de valeur, elle peut être convertie en l’un des types délégués Action ; sinon, elle peut être convertie en l’un des types délégués Func.If a lambda expression doesn't return a value, it can be converted to one of the Action delegate types; otherwise, it can be converted to one of the Func delegate types. Par exemple, une expression lambda qui a deux paramètres et qui ne retourne aucune valeur peut être convertie en un délégué Action<T1,T2>.For example, a lambda expression that has two parameters and returns no value can be converted to an Action<T1,T2> delegate. Une expression lambda qui a un paramètre et qui retourne une valeur peut être convertie en un délégué Func<T,TResult>.A lambda expression that has one parameter and returns a value can be converted to a Func<T,TResult> delegate. Dans l’exemple suivant, l’expression lambda x => x * x, qui spécifie un paramètre nommé x et retourne la valeur du carré de x, est assignée à une variable d’un type délégué :In the following example, the lambda expression x => x * x, which specifies a parameter that’s named x and returns the value of x squared, is assigned to a variable of a delegate type:

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

Les expressions lambda peuvent également être converties en types d’arborescence d’expression, comme le montre l’exemple suivant :Expression lambdas also can be converted to the expression tree types, as the following example shows:

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

Vous pouvez utiliser des expressions lambda dans tout code qui requiert des instances de types délégués ou d’arborescences d’expressions, par exemple en tant qu’argument de la méthode Task.Run(Action) pour passer le code qui doit être exécuté en arrière-plan.You can use lambda expressions in any code that requires instances of delegate types or expression trees, for example as an argument to the Task.Run(Action) method to pass the code that should be executed in the background. Vous pouvez également utiliser des expressions lambda quand vous écrivez des expressions de requête LINQ, comme le montre l’exemple suivant :You also can use lambda expressions when you write LINQ query expressions, as the following example shows:

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 (par exemple, 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, for example in LINQ to Objects and LINQ to XML, the parameter is a delegate type System.Func<T,TResult>. Si la méthode Queryable.Select est appelée dans la classe System.Linq.Queryable (par exemple, 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, for example in LINQ to SQL, the parameter type is an expression tree type Expression<Func<TSource,TResult>>. Dans les deux cas, vous pouvez utiliser la même expression lambda pour spécifier la valeur de paramètre.In both cases you can use the same lambda expression to specify the parameter value. Les deux appels Select semblent ainsi semblables alors qu’en fait le type des objets créés à partir des lambdas est différent.That makes the two Select calls to look similar although in fact the type of objects created from the lambdas is different.

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) => { <sequence-of-statements> }

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 ne peuvent pas être utilisées pour créer des arborescences d'expression.Statement lambdas 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.

Capture des variables externes et de l’étendue variable dans les expressions lambdaCapture of outer variables and variable scope in lambda expressions

Les expressions lambda peuvent faire référence à des variables externes.Lambdas can refer to outer variables. Il s’agit de variables dans l’étendue de la méthode qui définit l’expression lambda, ou dans l’étendue du type qui contient l’expression lambda.These are the variables 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