Wyrażenia lambda (C# Programming Guide)Lambda expressions (C# Programming Guide)

A wyrażenia lambda to blok kodu (wyrażenia lub blok instrukcji), który jest traktowany jako obiekt.A lambda expression is a block of code (an expression or a statement block) that is treated as an object. Może być przekazywany jako argument do metody, a także mogą być zwrócone przez funkcję wywołania metody.It can be passed as an argument to methods, and it can also be returned by method calls. Wyrażenia lambda są często używane do:Lambda expressions are used extensively for:

Wyrażenia lambda są kod, który może być reprezentowany jako obiekt delegowany lub jako drzewo wyrażenia, który kompiluje do delegata.Lambda expressions are code that can be represented either as a delegate, or as an expression tree that compiles to a delegate. Typ delegata określonego wyrażenia lambda jest zależna od jego parametrów i zwracają wartość.The specific delegate type of a lambda expression depends on its parameters and return value. Wyrażenia lambda, które nie zwracają wartości odpowiadają określonym Action delegować, w zależności od jego liczba parametrów.Lambda expressions that don't return a value correspond to a specific Action delegate, depending on its number of parameters. Wyrażenia lambda, które zwracają wartości odpowiadają określonym Func delegować, w zależności od jego liczba parametrów.Lambda expressions that return a value correspond to a specific Func delegate, depending on its number of parameters. Na przykład, wyrażenie lambda, która ma dwa parametry, ale nie zwraca żadnej wartości odnosi się do Action<T1,T2> delegować.For example, a lambda expression that has two parameters but returns no value corresponds to an Action<T1,T2> delegate. Wyrażenie lambda, która ma jeden parametr i zwraca wartość odpowiada Func<T,TResult> delegować.A lambda expression that has one parameter and returns a value corresponds to Func<T,TResult> delegate.

Używa wyrażenia lambda =>, operatora deklaracji lambda, aby oddzielić listą parametrów lambda z jego kodu wykonywalnego.A lambda expression uses =>, the lambda declaration operator, to separate the lambda's parameter list from its executable code. Aby utworzyć wyrażenie lambda, należy określić parametry wejściowe (jeśli istnieją) po lewej stronie operatora lambda i umieścić blok wyrażenia lub instrukcji po drugiej stronie.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. Na przykład, wyrażenie lambda jednowierszowego x => x * x określa parametr o nazwie x i zwraca wartość x kwadratów.For example, the single-line lambda expression x => x * x specifies a parameter that’s named x and returns the value of x squared. To wyrażenie można przypisać do typu delegata, tak jak pokazano w poniższym przykładzie: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

Można także przypisać wyrażenia lambda do Typ drzewa wyrażeń: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)

Lub można je przekazać bezpośrednio jako argumentu metody: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

Kiedy używasz składni oparte na metodzie do wywołania Enumerable.Select method in Class metoda System.Linq.Enumerable klasy (tak jak w technologii LINQ do obiektów i LINQ to XML) parametr jest typem delegowanym 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>. Użycie wyrażenia lambda jest najwygodniejszym sposobem tworzenia delegata.A lambda expression is the most convenient way to create that delegate. Gdy wywołujesz Queryable.Select method in Class metoda System.Linq.Queryable klasy (tak jak w składniku LINQ to SQL) typ parametru jest typ drzewa wyrażeń 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>>. I znowu wyrażenie lambda jest tylko bardzo zwięzłym sposobem konstruowania takiego drzewa wyrażeń.Again, a lambda expression is just a very concise way to construct that expression tree. Dzięki wyrażeniom lambda Select wywołania wyglądają podobnie, choć w rzeczywistości typ obiektu utworzonego z lambdy jest inny.The lambdas allow the Select calls to look similar although in fact the type of object created from the lambda is different.

Wszystkie ograniczenia, które są stosowane do anonimowymi dotyczą także wyrażeń lambda.All restrictions that apply to anonymous methods also apply to lambda expressions.

Lambdy wyrażeńExpression lambdas

Wyrażenie lambda z wyrażeniem po prawej stronie => operator jest nazywany wyrażenia lambda.A lambda expression with an expression on the right side of the => operator is called an expression lambda. Lambdy wyrażeń są często używane w konstrukcji drzew wyrażeń.Expression lambdas are used extensively in the construction of expression trees. Lambda wyrażenia zwraca wynik wyrażenia i ma następującą podstawową formę:An expression lambda returns the result of the expression and takes the following basic form:

(input-parameters) => expression

Nawiasy są opcjonalne tylko wtedy, gdy lambda ma jeden parametr wejściowy; w przeciwnym razie są wymagane.The parentheses are optional only if the lambda has one input parameter; otherwise they are required.

Określanie braku parametrów wejściowych za pomocą pustych nawiasów:Specify zero input parameters with empty parentheses:

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

Jeśli liczba parametrów wejściowych wynosi dwa lub więcej, te parametry są rozdzielane przecinkami i umieszczone w nawiasach:Two or more input parameters are separated by commas enclosed in parentheses:

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

Czasami niemożliwe jest dla kompilatora wywnioskowanie typów wejściowych.Sometimes it's impossible for the compiler to infer the input types. Można określić typy jawne, jak pokazano w poniższym przykładzie:You can specify the types explicitly as shown in the following example:

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

Typy parametru wejściowego muszą być wszystkie jawne lub niejawne; w przeciwnym razie CS0748 występuje błąd kompilatora.Input parameter types must be all explicit or all implicit; otherwise, a CS0748 compiler error occurs.

Treść wyrażenia lambda może zawierać wywołania metody.The body of an expression lambda can consist of a method call. Jednak w przypadku tworzenia drzew wyrażeń, które są obliczane poza kontekstem .NET środowiska uruchomieniowego języka wspólnego, takie jak w programie SQL Server, możesz nie należy używać metody wywołań w wyrażeniach 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. Te metody nie będą zrozumiałe poza kontekstem środowiska uruchomieniowego języka wspólnego platformy .NET.The methods will have no meaning outside the context of the .NET common language runtime.

Lambdy instrukcjiStatement lambdas

Lambda instrukcji jest podobna do lambdy wyrażenia, z tym że instrukcje są ujęte w nawiasy klamrowe:A statement lambda resembles an expression lambda except that the statement(s) is enclosed in braces:

(input-parameters) => { statement; }

Treść lambdy instrukcji może składać się z dowolnej liczby instrukcji, jednak w praktyce jest ich zwykle nie więcej niż dwie lub trzy.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!

Lambd instrukcji, podobnie jak metod anonimowych, nie można używać do tworzenia drzew wyrażeń.Statement lambdas, like anonymous methods, cannot be used to create expression trees.

Lambdy asynchroniczneAsync lambdas

Możesz łatwo tworzyć wyrażenia lambda i instrukcje, które zawierają Przetwarzanie asynchroniczne przy użyciu async i await słów kluczowych.You can easily create lambda expressions and statements that incorporate asynchronous processing by using the async and await keywords. Na przykład, poniższy przykład Windows Forms zawiera program obsługi zdarzeń, który wywołuje i czeka na metodę asynchroniczną 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);
    }
}

Można dodać ten sam program obsługi zdarzeń, używając lambdy asynchronicznej.You can add the same event handler by using an async lambda. Aby dodać ten program obsługi async modyfikator przed listą parametrów lambda, co ilustruje poniższy przykład: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);
    }
}

Aby uzyskać więcej informacji na temat sposobu tworzenia i używania metod asynchronicznych, zobacz Asynchronous Programming with async i await.For more information about how to create and use async methods, see Asynchronous Programming with async and await.

Wyrażenia lambda i krotkiLambda expressions and tuples

Począwszy od C# 7.0, C# język zapewnia wbudowaną obsługę krotek.Starting with C# 7.0, the C# language provides built-in support for tuples. Spójna Kolekcja może zapewnić jako argument do wyrażenia lambda i Wyrażenie lambda może również zwracać krotki.You can provide a tuple as an argument to a lambda expression, and your lambda expression can also return a tuple. W niektórych przypadkach kompilator języka C# używa wnioskowanie o typie, aby określić typy elementów krotki.In some cases, the C# compiler uses type inference to determine the types of tuple components.

Należy zdefiniować krotki umieszczając rozdzielana przecinkami lista składników w nawiasach.You define a tuple by enclosing a comma-delimited list of its components in parentheses. W poniższym przykładzie użyto krotki z trzech składników do przekazania w sekwencji liczb do wyrażenia lambda, rozwiązanie quorum zwiększa dwukrotnie każdej wartości, która zwraca krotki z trzech składników, który zawiera wynik mnożenia.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)

Zazwyczaj są nazywane polami krotki Item1, Item2itp. Można jednak zdefiniować krotki ze składnikami nazwane, tak jak w poniższym przykładzie.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}");

Aby uzyskać więcej informacji na temat C# krotek, zobacz C# typy krotki.For more information about C# tuples, see C# tuple types.

Lambdy ze standardowych operatorów zapytańLambdas with the standard query operators

LINQ do obiektów, między innymi implementacjami posiada parametr wejściowy, którego typ jest jednym z Func<TResult> rodziny ogólnych delegatów.LINQ to Objects, among other implementations, have an input parameter whose type is one of the Func<TResult> family of generic delegates. Ci delegaci używać parametrów typu, aby zdefiniować liczbę i typ parametrów danych wejściowych oraz zwracany typ delegata.These delegates use type parameters to define the number and type of input parameters, and the return type of the delegate. Func Obiekty delegowane są bardzo przydatne do hermetyzowania wyrażeń zdefiniowanych przez użytkownika, które są stosowane do każdego elementu w zestawie danych źródłowych.Func delegates are very useful for encapsulating user-defined expressions that are applied to each element in a set of source data. Na przykład, rozważmy Func<T,TResult> typ delegata:For example, consider the Func<T,TResult> delegate type:

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

Delegat może być utworzone jako Func<int, bool> wystąpienia gdzie int jest parametrem wejściowym i bool jest wartością zwracaną.The delegate can be instantiated as a Func<int, bool> instance where int is an input parameter and bool is the return value. Wartość zwracana jest zawsze określona w ostatnim parametrze typu.The return value is always specified in the last type parameter. Na przykład Func<int, string, bool> definiuje delegata z dwoma parametrami wejściowymi int i string, a zwracany typ bool.For example, Func<int, string, bool> defines a delegate with two input parameters, int and string, and a return type of bool. Następujące Func delegata, gdy jest wywoływana, zwraca wartość Boolean wskazującą, czy parametr wejściowy jest równy 5: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

Można również dostarczyć Wyrażenie lambda, gdy typ argumentu jest Expression<TDelegate>, na przykład w standardowych operatorów zapytań, które są zdefiniowane w Queryable typu.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. Po określeniu Expression<TDelegate> argument, wyrażenie lambda jest kompilowana do drzewa wyrażenie.When you specify an Expression<TDelegate> argument, the lambda is compiled to an expression tree.

W poniższym przykładzie użyto Count standardowego operatora zapytania: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)}");

Kompilator może wywnioskować typ parametru wejściowego, ale można go również określić w sposób jawny.The compiler can infer the type of the input parameter, or you can also specify it explicitly. To konkretne wyrażenie lambda liczy te liczby całkowite (n) które podzielone przez dwa dają resztę 1.This particular lambda expression counts those integers (n) which when divided by two have a remainder of 1.

Poniższy przykład tworzy sekwencję zawierającą wszystkie elementy w numbers tablica, która poprzedzać 9, ponieważ jest to pierwszy numer w sekwencji, która nie spełnia warunku: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

W poniższym przykładzie określono wiele parametrów danych wejściowych, umieszczając je w nawiasach.The following example specifies multiple input parameters by enclosing them in parentheses. Metoda ta zwraca wszystkie elementy w numbers tablicy, dopóki nie napotka liczby, w których wartość jest mniejsza niż jej porządkowym w tablicy: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

Wnioskowanie o typie w wyrażeniach lambdaType inference in lambda expressions

Podczas pisania wyrażeń lambda, często nie trzeba określać typu parametrów wejściowych, ponieważ kompilator może wywnioskować typ bazując na treść lambda, typy parametrów i inne czynniki, zgodnie z opisem w C# specyfikacji języka.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. Dla większości standardowych operatorów zapytań pierwszy element danych wejściowych jest typem elementów w sekwencji źródłowej.For most of the standard query operators, the first input is the type of the elements in the source sequence. Jeśli jest wykonywane zapytanie IEnumerable<Customer>, a następnie wywnioskowana jest zmienna wejściowa jest Customer obiektu, co oznacza, że masz dostęp do metod i właściwości: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");

Ogólne zasady wnioskowanie o typie dla wyrażeń lambda są następujące:The general rules for type inference for lambdas are as follows:

  • Wyrażenie lambda musi zawierać taką samą liczbę parametrów jak typ delegata.The lambda must contain the same number of parameters as the delegate type.

  • Każdy parametr wejściowy w wyrażeniu lambda musi umożliwiać niejawną konwersję na odpowiadający mu parametr delegata.Each input parameter in the lambda must be implicitly convertible to its corresponding delegate parameter.

  • Wartość zwracana wyrażenia lambda (jeżeli istnieje) musi umożliwiać niejawną konwersję na zwracany typ delegata.The return value of the lambda (if any) must be implicitly convertible to the delegate's return type.

Należy pamiętać, że wyrażenia lambda same w sobie nie mają typu, ponieważ wspólny system typów nie używa wewnętrznej koncepcji "wyrażenia lambda".Note that lambda expressions in themselves don't have a type because the common type system has no intrinsic concept of "lambda expression." Jednak czasami wygodnie jest mówić potocznie o "type" wyrażenia lambda.However, it's sometimes convenient to speak informally of the "type" of a lambda expression. W takich przypadkach typ odnosi się do typu delegata lub Expression typ do którego jest konwertowane Wyrażenie lambda.In these cases the type refers to the delegate type or Expression type to which the lambda expression is converted.

Zakres zmiennych w wyrażeniach lambdaVariable scope in lambda expressions

Wyrażenia lambda mogą odwoływać się do zmiennych zewnętrznych (zobacz anonimowymi) znajdujących się w zasięgu w metodzie, która definiuje wyrażenie lambda lub w zakresie typu, który zawiera wyrażenie 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. Przechwytywane w ten sposób zmienne są przechowywane do użytku w wyrażeniu lambda, nawet gdyby w innym wypadku te zmienne znalazłyby się poza zakresem i zostałyby usunięte w ramach odśmiecania pamięci.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. Zewnętrzna zmienna musi być zdecydowanie przypisana, aby można jej było użyć w wyrażeniu lambda.An outer variable must be definitely assigned before it can be consumed in a lambda expression. W poniższym przykładzie pokazano te reguły: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
}

Do zakresu zmiennych w wyrażeniach lambda są stosowane następujące reguły:The following rules apply to variable scope in lambda expressions:

  • Zmienna, która jest przechwytywana, nie będzie usuwana w ramach odśmiecania pamięci, dopóki odwołujący się do niej delegat nie będzie podlegał odśmiecaniu pamięci.A variable that is captured will not be garbage-collected until the delegate that references it becomes eligible for garbage collection.

  • Zmienne zawarte w wyrażeniu lambda nie są widoczne w otaczającej go metodzie.Variables introduced within a lambda expression are not visible in the enclosing method.

  • Wyrażenie lambda nie może bezpośrednio przechwycić w, ref, lub się parametr z otaczającym metody.A lambda expression cannot directly capture an in, ref, or out parameter from the enclosing method.

  • A zwracają instrukcji w wyrażeniu lambda nie powoduje otaczającej go metodzie do zwrócenia.A return statement in a lambda expression doesn't cause the enclosing method to return.

  • Wyrażenie lambda nie może zawierać goto, podziału, lub nadal instrukcji, jeśli obiekt docelowy, szybkie instrukcji znajduje się poza blokiem wyrażenia 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. Jest również błędu, aby mieć instrukcja skoku poza blok wyrażenia lambda, jeśli element docelowy znajduje się wewnątrz bloku.It's also an error to have a jump statement outside the lambda expression block if the target is inside the block.

specyfikacja języka C#C# language specification

Aby uzyskać więcej informacji, zobacz wyrażenia funkcji anonimowych części C# specyfikacji języka.For more information, see the Anonymous function expressions section of the C# language specification.

Delegatów, zdarzeń i wyrażenia Lambda w C# 3.0 Cookbook, Third Edition: Ponad 250 rozwiązań dla C# ekspertów w programowaniu w wersji 3.0Delegates, Events, and Lambda Expressions in C# 3.0 Cookbook, Third Edition: More than 250 solutions for C# 3.0 programmers

Zobacz takżeSee also