Výrazy lambda (C# Průvodce programováním)Lambda expressions (C# Programming Guide)

Výraz lambda je výraz kterékoli z následujících dvou forem:A lambda expression is an expression of any of the following two forms:

  • Výraz lambda výrazu , který má výraz jako tělo:Expression lambda that has an expression as its body:

    (input-parameters) => expression
    
  • Výraz lambda , který má blok příkazu jako jeho tělo:Statement lambda that has a statement block as its body:

    (input-parameters) => { <sequence-of-statements> }
    

Pomocí => operátoru deklarace lambda oddělte seznam parametrů lambda od jeho těla.Use the lambda declaration operator => to separate the lambda's parameter list from its body. Chcete-li vytvořit výraz lambda, zadejte vstupní parametry (pokud existují) na levé straně operátoru lambda a výraz nebo blok příkazu na druhé straně.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.

Libovolný výraz lambda lze převést na typ delegáta .Any lambda expression can be converted to a delegate type. Typ delegáta, na který lze výraz lambda převést, je definován typy jeho parametrů a návratové hodnoty.The delegate type to which a lambda expression can be converted is defined by the types of its parameters and return value. Pokud výraz lambda nevrací hodnotu, lze jej převést na jeden z Action typů delegátů. v opačném případě jej lze převést na jeden Func z typů delegátů.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. Například lambda výraz, který má dva parametry a nevrací žádnou hodnotu, lze převést na Action<T1,T2> delegáta.For example, a lambda expression that has two parameters and returns no value can be converted to an Action<T1,T2> delegate. Lambda výraz, který má jeden parametr a vrací hodnotu, lze převést na Func<T,TResult> delegáta.A lambda expression that has one parameter and returns a value can be converted to a Func<T,TResult> delegate. V následujícím příkladu lambda výraz x => x * x, který určuje parametr s názvem x x a vrací hodnotu Square, je přiřazen proměnné typu delegáta: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

Lambda výrazy lze také převést na typy stromu výrazů , jak ukazuje následující příklad: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)

Můžete použít výrazy lambda v jakémkoli kódu, který vyžaduje instance typů delegátů nebo stromů výrazů, například jako argument Task.Run(Action) metody k předání kódu, který by měl být proveden na pozadí.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. Lambda výrazy můžete použít také při psaní výrazů LINQ Query, jak ukazuje následující příklad: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

Použijete-li syntaxi založenou na metodě pro Enumerable.Select volání metody System.Linq.Enumerable ve třídě, například v LINQ to Objects a LINQ to XML, je parametr typu System.Func<T,TResult>delegát.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>. Při volání Queryable.Select metody System.Linq.Queryable ve třídě, například v LINQ to SQL, je typ parametru typ Expression<Func<TSource,TResult>>stromu výrazu.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>>. V obou případech můžete použít stejný výraz lambda k zadání hodnoty parametru.In both cases you can use the same lambda expression to specify the parameter value. To umožňuje, aby Select dvě volání vypadaly podobně, i když ve skutečnosti se typ objektů vytvořených z výrazů lambda liší.That makes the two Select calls to look similar although in fact the type of objects created from the lambdas is different.

Výrazy lambda výrazuExpression lambdas

Výraz lambda s výrazem na pravé straně => operátoru se nazývá výrazová lambda.A lambda expression with an expression on the right side of the => operator is called an expression lambda. Výrazy lambda výrazů se v konstrukci stromů výrazůpoužívají rozsáhle.Expression lambdas are used extensively in the construction of expression trees. Výrazová lambda vrátí výsledek výrazu a má následující základní podobu:An expression lambda returns the result of the expression and takes the following basic form:

(input-parameters) => expression

Závorky jsou nepovinné pouze v případě, že lambda má jeden vstupní parametr; v opačném případě jsou vyžadovány.The parentheses are optional only if the lambda has one input parameter; otherwise they are required.

Zadejte nulové vstupní parametry s prázdnými závorkami:Specify zero input parameters with empty parentheses:

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

Dva nebo více vstupních parametrů je odděleno čárkami, které jsou uvedeny v závorkách:Two or more input parameters are separated by commas enclosed in parentheses:

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

V některých případech je možné, že kompilátor odvodí vstupní typy.Sometimes it's impossible for the compiler to infer the input types. Můžete určit typy explicitně, jak je znázorněno v následujícím příkladu: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 vstupních parametrů musí být všechny explicitní nebo všechny implicitní; v opačném případě dojde k chybě kompilátoru CS0748 .Input parameter types must be all explicit or all implicit; otherwise, a CS0748 compiler error occurs.

Tělo výrazu lambda se může skládat z volání metody.The body of an expression lambda can consist of a method call. Pokud však vytváříte stromy výrazů, které jsou vyhodnocovány mimo kontext modulu CLR (Common Language Runtime) .NET, například v SQL Server, neměli byste používat volání metody ve výrazech 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. Metody nemají význam mimo kontext modulu CLR (Common Language Runtime) rozhraní .NET.The methods will have no meaning outside the context of the .NET common language runtime.

Výrazy lambda příkazůStatement lambdas

Příkazová lambda se podobá výrazové lambdě s tím rozdílem, že výrazy jsou uzavřeny ve složených závorkách:A statement lambda resembles an expression lambda except that the statement(s) is enclosed in braces:

(input-parameters) => { <sequence-of-statements> }

Text příkazové lambdy může obsahovat libovolný počet příkazů. V praxi jich však není obvykle více než dva nebo tři.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!

Výrazy lambda příkazu nejde použít k vytváření stromů výrazů.Statement lambdas cannot be used to create expression trees.

Asynchronní výrazy lambdaAsync lambdas

Můžete snadno vytvořit výrazy lambda a příkazy, které zahrnují asynchronní zpracování pomocí klíčových slov Async a await .You can easily create lambda expressions and statements that incorporate asynchronous processing by using the async and await keywords. Například následující příklad model Windows Forms obsahuje obslužnou rutinu události, která volá a očekává asynchronní metodu, 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);
    }
}

Stejný ovladač událostí můžete přidat pomocí asynchronní lambdy.You can add the same event handler by using an async lambda. Chcete-li přidat tuto obslužnou rutinu, přidejte async modifikátor před seznam parametrů lambda, jak ukazuje následující příklad: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);
    }
}

Další informace o tom, jak vytvořit a používat asynchronní metody, naleznete v tématu asynchronní programování s modifikátorem Async a await.For more information about how to create and use async methods, see Asynchronous Programming with async and await.

Výrazy lambda a řazené kolekce členůLambda expressions and tuples

Od verze C# 7,0 poskytuje C# jazyk integrovanou podporu pro řazené kolekce členů.Starting with C# 7.0, the C# language provides built-in support for tuples. Můžete zadat řazenou kolekci členů jako argument pro lambda výraz a váš výraz lambda může také vrátit řazenou kolekci členů.You can provide a tuple as an argument to a lambda expression, and your lambda expression can also return a tuple. V některých případech C# kompilátor používá odvození typu k určení typů komponent řazené kolekce členů.In some cases, the C# compiler uses type inference to determine the types of tuple components.

Řazenou kolekci členů můžete definovat tak, že v závorkách seřadíte seznam jeho komponent oddělených čárkami.You define a tuple by enclosing a comma-delimited list of its components in parentheses. Následující příklad používá řazené kolekce členů se třemi komponenty k předání sekvence čísel lambda výrazu, který zdvojnásobuje každou hodnotu a vrátí řazenou kolekci členů se třemi komponentami, které obsahují výsledek násobení.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)

Obvykle se pole řazené kolekce členů Item1nazývají, Item2atd. Můžete však definovat řazenou kolekci členů s pojmenovanými součástmi, jak je uvedeno v následujícím příkladu.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}");

Další informace o C# řazených kolekcích členů naleznete v tématu C# typy řazené kolekce členů.For more information about C# tuples, see C# tuple types.

Výrazy lambda se standardními operátory dotazuLambdas with the standard query operators

LINQ to Objects, mimo jiné implementace, má vstupní parametr, jehož typ je jeden z Func<TResult> řad generických delegátů.LINQ to Objects, among other implementations, have an input parameter whose type is one of the Func<TResult> family of generic delegates. Tito Delegáti používají parametry typu pro definování počtu a typu vstupních parametrů a návratový typ delegáta.These delegates use type parameters to define the number and type of input parameters, and the return type of the delegate. FuncDelegáti jsou velmi užiteční pro zapouzdření uživatelsky definovaných výrazů, které jsou aplikovány na každý prvek v sadě zdrojových dat.Func delegates are very useful for encapsulating user-defined expressions that are applied to each element in a set of source data. Zvažte například typ Func<T,TResult> delegáta:For example, consider the Func<T,TResult> delegate type:

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

Delegát může být vytvořen jako Func<int, bool> instance, kde int je vstupní parametr a bool je návratovou hodnotou.The delegate can be instantiated as a Func<int, bool> instance where int is an input parameter and bool is the return value. Vrácená hodnota je vždy určena v posledním parametru typu.The return value is always specified in the last type parameter. Například Func<int, string, bool> definuje delegáta se dvěma vstupními parametry string, int a a návratový typ bool.For example, Func<int, string, bool> defines a delegate with two input parameters, int and string, and a return type of bool. Následující Func delegát, pokud je vyvolána, vrátí logickou hodnotu, která označuje, zda je vstupní parametr roven pěti: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

Výraz lambda lze také zadat Expression<TDelegate>, pokud je typ argumentu, například ve standardních operátorech dotazu, které jsou definovány Queryable v 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. Při zadání Expression<TDelegate> argumentu je výraz lambda zkompilován do stromu výrazu.When you specify an Expression<TDelegate> argument, the lambda is compiled to an expression tree.

Následující příklad používá Count standardní operátor dotazu: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)}");

Kompilátor může odvodit typ vstupního parametru, nebo jej můžete nastavit také explicitně.The compiler can infer the type of the input parameter, or you can also specify it explicitly. Tento konkrétní výraz lambda počítá tato celá čísla (n), která při vydělení dvěma mají zbytek 1.This particular lambda expression counts those integers (n) which when divided by two have a remainder of 1.

Následující příklad vytvoří sekvenci, která obsahuje všechny prvky v numbers poli, které předcházejí hodnotě 9, protože se jedná o první číslo v sekvenci, která nesplňuje podmínku: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

Následující příklad určuje více vstupních parametrů uzavřením do závorek.The following example specifies multiple input parameters by enclosing them in parentheses. Metoda vrátí všechny prvky v numbers poli, dokud nenarazí na číslo, jehož hodnota je menší než pořadové místo v poli: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

Odvození typu ve výrazech lambdaType inference in lambda expressions

Při psaní výrazů lambda není často nutné zadat typ pro vstupní parametry, protože kompilátor může odvodit typ na základě těla lambda, typů parametrů a dalších faktorů, jak je popsáno ve specifikaci C# jazyka.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. Pro většinu standardních operátorů pro dotazování je prvním vstupem typ prvků ve zdrojové sekvenci.For most of the standard query operators, the first input is the type of the elements in the source sequence. Pokud se dotazuje IEnumerable<Customer>na, pak vstupní proměnná je odvozena Customer jako objekt, což znamená, že máte přístup ke svým metodám a vlastnostem: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");

Obecná pravidla pro odvození typu pro výrazy lambda jsou následující:The general rules for type inference for lambdas are as follows:

  • Výraz lambda musí obsahovat stejný počet parametrů jako typ delegátu.The lambda must contain the same number of parameters as the delegate type.

  • Každý vstupní parametr ve výrazu lambda musí být implicitně převoditelný na odpovídající parametr delegátu.Each input parameter in the lambda must be implicitly convertible to its corresponding delegate parameter.

  • Vrácená hodnota lambda (pokud existuje) musí být implicitně převoditelná na návratový typ delegátu.The return value of the lambda (if any) must be implicitly convertible to the delegate's return type.

Všimněte si, že lambda výrazy samy nemají typ, protože společný typ systému nemá žádný vnitřní koncept výrazu lambda.Note that lambda expressions in themselves don't have a type because the common type system has no intrinsic concept of "lambda expression." Někdy je však vhodné mluvit neformálně jako "typ" výrazu lambda.However, it's sometimes convenient to speak informally of the "type" of a lambda expression. V těchto případech typ odkazuje na typ delegáta nebo Expression typ, na který je výraz lambda převeden.In these cases the type refers to the delegate type or Expression type to which the lambda expression is converted.

Zachycení vnějších proměnných a rozsahu proměnné ve výrazech lambdaCapture of outer variables and variable scope in lambda expressions

Výrazy lambda mohou odkazovat na vnější proměnné.Lambdas can refer to outer variables. Toto jsou proměnné, které jsou v oboru v metodě definující výraz lambda nebo v rozsahu typu, který obsahuje výraz 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. Proměnné, které jsou zachyceny tímto způsobem, jsou uloženy pro použití ve výrazu lambda i v případě, že proměnné by jinak přesáhly rozsah platnosti a bylo by vynuceno uvolnění paměti.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. Vnější proměnná musí být jednoznačně přiřazena dříve, než může být upotřebena ve výrazu lambda.An outer variable must be definitely assigned before it can be consumed in a lambda expression. Následující příklad znázorňuje tato pravidla: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
}

Následující pravidla se vztahují na rozsah proměnné ve výrazu lambda:The following rules apply to variable scope in lambda expressions:

  • Proměnná, která je zachycena, nebude uvolněna z paměti, dokud delegát, který na ni odkazuje, nebude mít oprávnění k uvolňování paměti.A variable that is captured will not be garbage-collected until the delegate that references it becomes eligible for garbage collection.

  • Proměnné, které jsou představeny v rámci výrazu lambda, nejsou viditelné v nadřazené metodě.Variables introduced within a lambda expression are not visible in the enclosing method.

  • Výraz lambda nemůže přímo zachytit parametr in, refnebo out z nadřazené metody.A lambda expression cannot directly capture an in, ref, or out parameter from the enclosing method.

  • Příkaz return ve výrazu lambda nezpůsobí vrácení ohraničující metody.A return statement in a lambda expression doesn't cause the enclosing method to return.

  • Výraz lambda nemůže obsahovat příkaz goto, Breaknebo Continue , pokud cíl tohoto příkazu skoku je mimo blok výrazu 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. Je také možné, že příkaz skoku je mimo blok výrazu lambda, pokud je cíl uvnitř bloku.It's also an error to have a jump statement outside the lambda expression block if the target is inside the block.

specifikace jazyka C#C# language specification

Další informace naleznete v části výrazy anonymní funkce C# specifikace jazyka.For more information, see the Anonymous function expressions section of the C# language specification.

Delegáti, události a výrazy lambda v C# 3,0 kuchařka, třetí vydání: Více než 250 řešení pro C# 3,0 programátoryDelegates, Events, and Lambda Expressions in C# 3.0 Cookbook, Third Edition: More than 250 solutions for C# 3.0 programmers

Viz také:See also