Espressioni lambda (Guida per programmatori C#)Lambda expressions (C# Programming Guide)

Un'espressione lambda è un blocco di codice costituito da espressioni o da istruzioni che viene trattato come oggetto.A lambda expression is a block of code (an expression or a statement block) that is treated as an object. Tale blocco può essere passato come argomento ai metodi e può anche essere restituito dalle chiamate al metodo.It can be passed as an argument to methods, and it can also be returned by method calls. Le espressioni lambda sono spesso usate per eseguire le attività seguenti:Lambda expressions are used extensively for:

Le espressioni lambda sono codice che può essere rappresentato come delegato o come albero delle espressioni che viene compilato in un delegato.Lambda expressions are code that can be represented either as a delegate, or as an expression tree that compiles to a delegate. Il tipo di delegato specifico di un'espressione lambda dipende dai parametri dell'espressione e dal valore restituito.The specific delegate type of a lambda expression depends on its parameters and return value. Le espressioni lambda che non restituiscono un valore corrispondano a un delegato Action specifico, che dipende dal relativo numero di parametri.Lambda expressions that don't return a value correspond to a specific Action delegate, depending on its number of parameters. Le espressioni lambda che restituiscono un valore corrispondano a un delegato Func specifico, che dipende dal relativo numero di parametri.Lambda expressions that return a value correspond to a specific Func delegate, depending on its number of parameters. Se ad esempio un'espressione lambda con due parametri non restituisce un valore, corrisponde a un delegato Action<T1,T2>.For example, a lambda expression that has two parameters but returns no value corresponds to an Action<T1,T2> delegate. Se un'espressione lambda ha un parametro e restituisce un valore, corrisponde a un delegato Func<T,TResult>.A lambda expression that has one parameter and returns a value corresponds to Func<T,TResult> delegate.

Un'espressione lambda usa =>, vale a dire l'operatore di dichiarazione lambda, per separare l'elenco di parametri dell'espressione lambda dal codice eseguibile.A lambda expression uses =>, the lambda declaration operator, to separate the lambda's parameter list from its executable code. Per creare un'espressione lambda, specificare gli eventuali parametri di input a sinistra dell'operatore lambda e inserire il blocco di espressioni o di istruzioni dall'altra parte.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. Ad esempio, l'espressione lambda x => x * x a riga singola specifica un parametro denominato x e restituisce il valore di x al quadrato.For example, the single-line lambda expression x => x * x specifies a parameter that’s named x and returns the value of x squared. È possibile assegnare questa espressione a un tipo di delegato, come illustrato nell'esempio riportato di seguito: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

È anche possibile assegnare un'espressione lambda a un tipo di albero delle espressioni: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)

Oppure è possibile passarla direttamente come argomento del metodo: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

Quando si usa la sintassi basata sul metodo per chiamare il metodo Enumerable.Select nella classe System.Linq.Enumerable, come in LINQ to Objects e LINQ to XML, il parametro è un tipo delegato 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>. Un'espressione lambda è il modo più pratico per creare tale delegato.A lambda expression is the most convenient way to create that delegate. Quando si chiama il metodo Queryable.Select nella classe System.Linq.Queryable, come in LINQ to SQL, il tipo di parametro è un tipo di albero delle espressioni 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>>. Un'espressione lambda rappresenta quindi un modo rapido per costruire tale albero delle espressioni.Again, a lambda expression is just a very concise way to construct that expression tree. Mediante le espressioni lambda, le chiamate Select risultano simili anche se in realtà il tipo di oggetto creato dall'espressione lambda è diverso.The lambdas allow the Select calls to look similar although in fact the type of object created from the lambda is different.

Tutte le restrizioni che si applicano ai metodi anonimi si applicano anche alle espressioni lambda.All restrictions that apply to anonymous methods also apply to lambda expressions.

Espressioni lambdaExpression lambdas

Un'espressione lambda con un'espressione a destra dell'operatore => è denominata espressione lambda.A lambda expression with an expression on the right side of the => operator is called an expression lambda. Queste espressioni vengono usate spesso nella costruzione di alberi delle espressioni.Expression lambdas are used extensively in the construction of expression trees. Un'espressione lambda dell'espressione restituisce il risultato dell'espressione e ha il formato di base seguente:An expression lambda returns the result of the expression and takes the following basic form:

(input-parameters) => expression

Le parentesi sono facoltative solo se l'espressione lambda ha un parametro di input; in caso contrario sono obbligatorie.The parentheses are optional only if the lambda has one input parameter; otherwise they are required.

Specificare zero parametri di input con parentesi vuote:Specify zero input parameters with empty parentheses:

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

Due o più parametri di input vengono separati da virgole e racchiusi tra parentesi:Two or more input parameters are separated by commas enclosed in parentheses:

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

Talvolta è impossibile che il compilatore deduca i tipi di input.Sometimes it's impossible for the compiler to infer the input types. È possibile specificare i tipi in modo esplicito come illustrato nell'esempio seguente:You can specify the types explicitly as shown in the following example:

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

I parametri di input devono essere tutti di tipo esplicito o tutti di tipo implicito. In caso contrario, si verifica un errore del compilatore CS0748.Input parameter types must be all explicit or all implicit; otherwise, a CS0748 compiler error occurs.

Il corpo di un'espressione lambda può essere costituito da una chiamata al metodo.The body of an expression lambda can consist of a method call. Se tuttavia si creano alberi delle espressioni valutati al di fuori del contesto di Common Language Runtime di .NET Framework, ad esempio in SQL Server, non è consigliabile usare chiamate al metodo nelle espressioni 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. I metodi non avranno alcun significato all'esterno del contesto di .NET Common Language Runtime.The methods will have no meaning outside the context of the .NET common language runtime.

Espressioni lambda dell'istruzioneStatement lambdas

Un'espressione lambda dell'istruzione è simile a un'espressione lambda dell'espressione con la differenza che l'istruzione o le istruzioni sono racchiuse tra parentesi graffe:A statement lambda resembles an expression lambda except that the statement(s) is enclosed in braces:

(input-parameters) => { statement; }

Il corpo di un'espressione lambda dell'istruzione può essere costituito da un numero qualsiasi di istruzioni, sebbene in pratica generalmente non ce ne siano più di due o tre.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!

Le espressioni lambda dell'istruzione, come i metodi anonimi, non possono essere utilizzate per creare alberi delle espressioni.Statement lambdas, like anonymous methods, cannot be used to create expression trees.

Espressioni lambda asincroneAsync lambdas

È facile creare istruzioni ed espressioni lambda che includono l'elaborazione asincrona utilizzando le parole chiave async e await .You can easily create lambda expressions and statements that incorporate asynchronous processing by using the async and await keywords. Nell'esempio seguente di Windows Form è presente un gestore eventi che chiama e attende un metodo asincrono, 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);
    }
}

È possibile aggiungere lo stesso gestore eventi utilizzando un'espressione lambda asincrona.You can add the same event handler by using an async lambda. Per aggiungere il gestore, aggiungere un modificatore async prima dell'elenco di parametri lambda, come illustrato di seguito: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);
    }
}

Per altre informazioni su come creare e usare i metodi asincroni, vedere Programmazione asincrona con async e await.For more information about how to create and use async methods, see Asynchronous Programming with async and await.

Espressioni lambda e tupleLambda expressions and tuples

A partire da C# 7.0, il linguaggio C# offre supporto predefinito per le tuple.Starting with C# 7.0, the C# language provides built-in support for tuples. È possibile specificare una tupla come argomento di un'espressione lambda e l'espressione lambda può restituire una tupla.You can provide a tuple as an argument to a lambda expression, and your lambda expression can also return a tuple. In alcuni casi, il compilatore C# usa l'inferenza del tipo per determinare i tipi di componenti della tupla.In some cases, the C# compiler uses type inference to determine the types of tuple components.

Per definire una tupla, è necessario racchiudere tra parentesi un elenco di componenti delimitato da virgole.You define a tuple by enclosing a comma-delimited list of its components in parentheses. L'esempio riportato sotto usa una tupla con tre componenti per passare una sequenza di numeri a un'espressione lambda, la quale raddoppia ogni valore e restituisce una tupla con tre componenti che contiene il risultato delle moltiplicazioni.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)

In genere, i campi di una tupla sono denominati Item1, Item2 e così via. È possibile tuttavia definire una tupla usando i componenti denominati, come illustra l'esempio seguente.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}");

Per altre informazioni sulle tuple in C#, vedere Tipi tupla in C#.For more information about C# tuples, see C# tuple types.

Espressioni lambda con operatori query standardLambdas with the standard query operators

LINQ to Objects, tra altre implementazioni, ha un parametro di input il cui tipo appartiene alla famiglia Func<TResult> di delegati generici.LINQ to Objects, among other implementations, have an input parameter whose type is one of the Func<TResult> family of generic delegates. Questi delegati usano parametri di tipo per definire il numero e il tipo di parametri di input e il tipo restituito del delegato.These delegates use type parameters to define the number and type of input parameters, and the return type of the delegate. I delegatiFunc sono molto utili per incapsulare le espressioni definite dall'utente applicate a ogni elemento in un set di dati di origine.Func delegates are very useful for encapsulating user-defined expressions that are applied to each element in a set of source data. Considerare ad esempio il tipo delegato Func<T,TResult>:For example, consider the Func<T,TResult> delegate type:

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

È possibile creare un'istanza Func<int, bool> del delegato, dove int è un parametro di input e bool è il valore restituito.The delegate can be instantiated as a Func<int, bool> instance where int is an input parameter and bool is the return value. Il valore restituito è sempre specificato nell'ultimo parametro di tipo.The return value is always specified in the last type parameter. Func<int, string, bool>, ad esempio, definisce un delegato con due parametri di input, int e string, e un tipo restituito bool.For example, Func<int, string, bool> defines a delegate with two input parameters, int and string, and a return type of bool. Quando viene richiamato, il delegato Func seguente restituisce il valore booleano indicante se il parametro di input è uguale a cinque: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

È anche possibile specificare un'espressione lambda quando il tipo di argomento è Expression<TDelegate>, ad esempio negli operatori query standard definiti nel tipo 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. Quando si specifica un argomento Expression<TDelegate>, l'espressione lambda viene compilata per un albero delle espressioni.When you specify an Expression<TDelegate> argument, the lambda is compiled to an expression tree.

L'esempio seguente usa l'operatore query 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)}");

Il compilatore è in grado di dedurre il tipo del parametro di input oppure è possibile specificarlo in modo esplicito.The compiler can infer the type of the input parameter, or you can also specify it explicitly. Questa espressione lambda particolare conta i numeri interi (n) che divisi per due danno il resto di 1.This particular lambda expression counts those integers (n) which when divided by two have a remainder of 1.

L'esempio seguente crea una sequenza contenente tutti gli elementi presenti nella matrice numbers che si trovano a sinistra di 9, vale a dire il primo numero della sequenza che non soddisfa la condizione: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

In questo esempio viene illustrato come specificare più parametri di input racchiudendoli tra parentesi.The following example specifies multiple input parameters by enclosing them in parentheses. Il metodo restituisce tutti gli elementi presenti nella matrice numbers finché non viene rilevato un numero il cui valore è inferiore alla relativa posizione all'interno della matrice: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

Inferenza del tipo nelle espressioni lambdaType inference in lambda expressions

Quando si scrivono espressioni lambda, spesso non occorre specificare un tipo per i parametri di input, perché il compilatore può dedurlo in base al corpo dell'espressione lambda, ai tipi di parametro e ad altri fattori, come descritto nelle specifiche del linguaggio 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. Per la maggior parte degli operatori di query standard, il primo input è il tipo degli elementi nella sequenza di origine.For most of the standard query operators, the first input is the type of the elements in the source sequence. Pertanto, se si esegue una query su un oggetto IEnumerable<Customer>, si deduce che la variabile di input sia un oggetto Customer, ovvero che si dispone dell'accesso ai relativi metodi e proprietà: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");

Di seguito sono riportate le regole generali per l'inferenza del tipo nelle espressioni lambda:The general rules for type inference for lambdas are as follows:

  • L'espressione lambda deve contenere lo stesso numero di parametri del tipo delegato.The lambda must contain the same number of parameters as the delegate type.

  • Ogni parametro di input nell'espressione lambda deve essere convertibile in modo implicito nel parametro del delegato corrispondente.Each input parameter in the lambda must be implicitly convertible to its corresponding delegate parameter.

  • Il valore restituito dell'espressione lambda, se presente, deve essere convertibile in modo implicito nel tipo restituito del delegato.The return value of the lambda (if any) must be implicitly convertible to the delegate's return type.

Si noti che le espressioni lambda non hanno un tipo perché Common Type System non ha alcun concetto intrinseco di "espressione lambda".Note that lambda expressions in themselves don't have a type because the common type system has no intrinsic concept of "lambda expression." In alcuni casi, tuttavia, può essere utile fare riferimento in modo informale al "tipo" di un'espressione lambda.However, it's sometimes convenient to speak informally of the "type" of a lambda expression. In questi casi, per tipo si intende il tipo delegato o il tipo Expression in cui viene convertita l'espressione lambda.In these cases the type refers to the delegate type or Expression type to which the lambda expression is converted.

Ambito delle variabili nelle espressioni lambdaVariable scope in lambda expressions

Le espressioni lambda possono fare riferimento alle variabili esterne (vedere Metodi anonimi) presenti nell'ambito del metodo che definisce l'espressione lambda oppure nell'ambito del tipo che contiene l'espressione 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. Le variabili acquisite in questo modo vengono archiviate per poter essere utilizzate nell'espressione lambda anche se le variabili diventano esterne all'ambito e vengono sottoposte a Garbage Collection.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. Una variabile esterna deve essere assegnata prima di poter essere utilizzata in un'espressione lambda.An outer variable must be definitely assigned before it can be consumed in a lambda expression. Nell'esempio seguente vengono illustrate queste regole: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
}

Le regole seguenti si applicano all'ambito delle variabili nelle espressioni lambda:The following rules apply to variable scope in lambda expressions:

  • Una variabile acquisita non sarà sottoposta a Garbage Collection finché il delegato a cui fa riferimento non diventa idoneo per il Garbage Collection.A variable that is captured will not be garbage-collected until the delegate that references it becomes eligible for garbage collection.

  • Le variabili introdotte in un'espressione lambda non sono visibili nel metodo contenitore.Variables introduced within a lambda expression are not visible in the enclosing method.

  • Un'espressione lambda non può acquisire direttamente un parametro in, ref o out dal metodo contenitore.A lambda expression cannot directly capture an in, ref, or out parameter from the enclosing method.

  • Un'istruzione return in un'espressione lambda non causa la restituzione del metodo contenitore.A return statement in a lambda expression doesn't cause the enclosing method to return.

  • Un'espressione lambda non può contenere un'istruzione goto, break o continue se la destinazione di tale istruzione di salto è esterna al blocco dell'espressione 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. È anche errato inserire all'esterno del blocco dell'espressione lambda un'istruzione di salto se la destinazione è interna al blocco.It's also an error to have a jump statement outside the lambda expression block if the target is inside the block.

Specifiche del linguaggio C#C# language specification

Per altre informazioni, vedere la sezione Espressioni di funzioni anonime della specifica del linguaggio C#.For more information, see the Anonymous function expressions section of the C# language specification.

Delegates, Events, and Lambda Expressions (Delegati, eventi ed espressioni Lambda) in C# 3.0 Cookbook, Third Edition: More than 250 solutions for C# 3.0 programmers (Tutto su C# 3.0, terza edizione: più di 250 soluzioni per i programmatori C# 3.0)Delegates, Events, and Lambda Expressions in C# 3.0 Cookbook, Third Edition: More than 250 solutions for C# 3.0 programmers

Vedere ancheSee also