Lambdaausdrücke (C#-Programmierhandbuch)Lambda expressions (C# Programming Guide)

Ein Lambdaausdruck ist ein Ausdruck in einer der beiden folgenden Formen:A lambda expression is an expression of any of the following two forms:

Verwenden Sie den Lambdadeklarationsoperator =>, um die Parameterliste des Lambdas von dessen Text zu trennen.Use the lambda declaration operator => to separate the lambda's parameter list from its body. Geben Sie zum Erstellen eines Lambdaausdrucks Eingabeparameter (falls vorhanden) auf der linken Seite des Lambdaoperators und einen Ausdruck oder Anweisungsblock auf der anderen Seite an.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.

Jeder Lambdaausdruck kann in einen Delegat-Typ konvertiert werden.Any lambda expression can be converted to a delegate type. Der Delegattyp, in den ein Lambdaausdruck konvertiert werden kann, wird durch die Typen seiner Parameter und Rückgabewerte definiert.The delegate type to which a lambda expression can be converted is defined by the types of its parameters and return value. Wenn ein Lambdaausdruck keinen Wert zurückgibt, kann er in einen der Action-Delegattypen konvertiert werden. Andernfalls kann er in einen der Func-Delegattypen konvertiert werden.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. Ein Lambdaausdruck, der beispielsweise zwei Parameter hat und keinen Wert zurückgibt, kann in einen Action<T1,T2>-Delegat konvertiert werden.For example, a lambda expression that has two parameters and returns no value can be converted to an Action<T1,T2> delegate. Außerdem kann ein Lambdaausdruck, der einen Parameter hat und einen Wert zurückgibt, in einen Func<T,TResult>-Delegat konvertiert werden.A lambda expression that has one parameter and returns a value can be converted to a Func<T,TResult> delegate. Im folgenden Beispiel wird der Lambdaausdruck x => x * x, der einen Parameter x angibt und den Wert von x im Quadrat zurückgibt, einer Variablen eines Delegattyps zugewiesen: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

Zudem lassen sich Ausdruckslambdas in Ausdrucksbaumstruktur-Typen konvertieren, wie im folgenden Beispiel gezeigt: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)

Sie können Lambdaausdrücke in jedem Code verwenden, der Instanzen von Delegattypen oder Ausdrucksbaumstrukturen erfordert, z.B. als Argument für die Task.Run(Action)-Methode, um den im Hintergrund auszuführenden Code zu übergeben.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. Beim Schreiben von LINQ-Abfrageausdrücken können Sie ebenfalls Lambdaausdrücke verwenden, wie im folgenden Beispiel gezeigt: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

Wenn Sie zum Aufrufen der Enumerable.Select-Methode in der System.Linq.Enumerable-Klasse methodenbasierte Syntax verwenden, wie in LINQ to Objects und LINQ to XML, ist der Parameter ein Delegattyp 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>. Wenn Sie die Queryable.Select-Methode in der System.Linq.Queryable-Klasse aufrufen, wie in LINQ to SQL, ist der Parametertyp ein Ausdrucksbaumstruktur-Typ 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>>. In beiden Fällen können Sie denselben Lambdaausdruck verwenden, um die Parameterwerte anzugeben.In both cases you can use the same lambda expression to specify the parameter value. Dadurch sehen die Select-Aufrufe ähnlich aus, obwohl sich der mithilfe der Lambdas erstellte Objekttyp unterscheidet.That makes the two Select calls to look similar although in fact the type of objects created from the lambdas is different.

AusdruckslambdasExpression lambdas

Ein Lambdaausdruck mit einem Ausdruck auf der rechten Seite des =>-Operators wird als Ausdruckslambda bezeichnet.A lambda expression with an expression on the right side of the => operator is called an expression lambda. Ausdruckslambdas werden häufig bei der Erstellung von Ausdrucksbaumstrukturen verwendet.Expression lambdas are used extensively in the construction of expression trees. Ein Ausdruckslambda gibt das Ergebnis des Ausdrucks zurück und hat folgende grundlegende Form:An expression lambda returns the result of the expression and takes the following basic form:

(input-parameters) => expression

Die Klammern sind nur optional, wenn das Lambda über einen Eingabeparameter verfügt; andernfalls sind sie erforderlich.The parentheses are optional only if the lambda has one input parameter; otherwise they are required.

Geben Sie Eingabeparameter von 0 (null) mit leeren Klammern an:Specify zero input parameters with empty parentheses:

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

Zwei oder mehr Eingabeparameter sind durch Kommas getrennt und in Klammern eingeschlossen:Two or more input parameters are separated by commas enclosed in parentheses:

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

Für den Compiler ist es manchmal unmöglich, die Eingabetypen abzuleiten.Sometimes it's impossible for the compiler to infer the input types. Sie können die Typen wie im folgenden Beispiel dargestellt explizit angeben:You can specify the types explicitly as shown in the following example:

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

Eingabeparametertypen müssen alle entweder explizit oder implizit sein. Andernfalls tritt der Compilerfehler CS0748 auf.Input parameter types must be all explicit or all implicit; otherwise, a CS0748 compiler error occurs.

Der Text eines Ausdruckslambdas kann aus einem Methodenaufruf bestehen.The body of an expression lambda can consist of a method call. Wenn Sie jedoch Ausdrucksbaumstrukturen erstellen, die außerhalb des Kontexts der .NET Common Language Runtime ausgewertet werden, z.B. in SQL Server, sollten Sie in Lambdaausdrücken keine Methodenaufrufe verwenden.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. Die Methoden haben außerhalb des Kontexts der .NET Common Language Runtime keine Bedeutung.The methods will have no meaning outside the context of the .NET common language runtime.

AnweisungslambdasStatement lambdas

Ein Anweisungslambda ähnelt einem Ausdruckslambda, allerdings sind die Anweisungen in Klammern eingeschlossen:A statement lambda resembles an expression lambda except that the statement(s) is enclosed in braces:

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

Der Text eines Anweisungslambdas kann aus einer beliebigen Anzahl von Anweisungen bestehen, wobei es sich meistens um höchstens zwei oder drei Anweisungen handelt.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!

Anweisungslambdas können nicht zum Erstellen von Ausdrucksbaumstrukturen verwendet werden.Statement lambdas cannot be used to create expression trees.

Asynchrone LambdasAsync lambdas

Sie können mit den Schlüsselwörtern async und await Lambda-Ausdrücke und Anweisungen, die asynchrone Verarbeitung enthalten, leicht erstellen.You can easily create lambda expressions and statements that incorporate asynchronous processing by using the async and await keywords. Das folgende Windows Forms enthält z. B. einen Ereignishandler, der eine Async-Methode, ExampleMethodAsync, aufruft und erwartet.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);
    }
}

Sie können denselben Ereignishandler hinzufügen, indem Sie ein asynchrones Lambda verwenden.You can add the same event handler by using an async lambda. Um diesen Handler hinzuzufügen, fügen Sie einen async-Modifizierer vor der Lambdaparameterliste hinzu, wie im folgenden Beispiel dargestellt: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);
    }
}

Weitere Informationen zum Erstellen und Verwenden von asynchronen Methoden finden Sie unter Asynchronous programming with async and await (Asynchrones Programmieren mit „async“ and „await“).For more information about how to create and use async methods, see Asynchronous Programming with async and await.

Lambdaausdrücke und TupelLambda expressions and tuples

Ab C# 7.0 bietet die C#-Programmiersprache integrierte Unterstützung für Tupel.Starting with C# 7.0, the C# language provides built-in support for tuples. Sie können ein Tupel einem Lambdaausdruck als Argument bereitstellen, und Ihr Lambdaausdruck kann ebenfalls einen Tupel zurückgeben.You can provide a tuple as an argument to a lambda expression, and your lambda expression can also return a tuple. In einigen Fällen verwendet der C#-Compiler den Typrückschluss, um den Typ der Tupelkomponenten zu ermitteln.In some cases, the C# compiler uses type inference to determine the types of tuple components.

Sie können ein Tupel definieren, indem Sie eine durch Trennzeichen getrennte Liste seiner Komponenten in Klammern einschließen.You define a tuple by enclosing a comma-delimited list of its components in parentheses. In folgendem Beispiel wird ein Tupel mit drei Komponenten verwenden, um eine Zahlensequenz an einen Lambdaausdruck zu übergeben; dadurch wird jeder Wert verdoppelt, und es wird ein Tupel mit drei Komponenten zurückgegeben, das das Ergebnis der Multiplikation enthält.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)

Für gewöhnlich heißen die Felder eines Tupels Item1, Item2, usw. Sie können allerdings ein Tupel mit benannten Komponenten definieren, wie in folgendem Beispiel veranschaulicht.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}");

Weitere Informationen zu C#-Tupeln finden Sie unter C#-Tupeltypen.For more information about C# tuples, see C# tuple types.

Lambdas mit StandardabfrageoperatorenLambdas with the standard query operators

LINQ to Objects haben, neben anderen Implementierungen, einen Eingabeparameter, dessen Typ Teil der Func<TResult>-Familie generischer Delegate ist.LINQ to Objects, among other implementations, have an input parameter whose type is one of the Func<TResult> family of generic delegates. Diese Delegaten verwenden Typparameter zur Definition der Anzahl und des Typs der Eingabeparameter sowie des Rückgabetyps des Delegaten.These delegates use type parameters to define the number and type of input parameters, and the return type of the delegate. Func -Delegaten sind für das Kapseln von benutzerdefinierten Ausdrücken, die für jedes Element in einem Satz von Quelldaten übernommen werden, sehr nützlich.Func delegates are very useful for encapsulating user-defined expressions that are applied to each element in a set of source data. Betrachten Sie z.B. den Func<T,TResult>-Delegattyp:For example, consider the Func<T,TResult> delegate type:

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

Der Delegat kann als Func<int, bool>-Instanz instanziiert werden, wobei int ein Eingabeparameter und bool der Rückgabewert ist.The delegate can be instantiated as a Func<int, bool> instance where int is an input parameter and bool is the return value. Der Rückgabewert wird immer im letzten Typparameter angegeben.The return value is always specified in the last type parameter. Func<int, string, bool> definiert z.B. einen Delegaten mit zwei Eingabeparametern, int und string, und einen Rückgabetyp von bool.For example, Func<int, string, bool> defines a delegate with two input parameters, int and string, and a return type of bool. Der folgende Func-Delegat gibt bei einem Aufruf einen booleschen Wert zurück, um anzugeben, ob der Eingabeparameter gleich fünf ist: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

Sie können einen Lambdaausdruck auch dann angeben, wenn der Argumenttyp Expression<TDelegate> ist, beispielsweise in den Standardabfrageoperatoren, die in Typ Queryable definiert sind.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. Wenn Sie ein Expression<TDelegate>-Argument angeben, wird der Lambdaausdruck in eine Ausdrucksbaumstruktur kompiliert.When you specify an Expression<TDelegate> argument, the lambda is compiled to an expression tree.

Im folgenden Beispiel wird der Count-Standardabfrageoperator verwendet: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)}");

Der Compiler kann den Typ des Eingabeparameters ableiten, Sie können ihn aber auch explizit angeben.The compiler can infer the type of the input parameter, or you can also specify it explicitly. Dieser bestimmte Lambda-Ausdruck zählt die ganzen Zahlen (n), bei denen nach dem Dividieren durch zwei als Rest 1 bleibt.This particular lambda expression counts those integers (n) which when divided by two have a remainder of 1.

In folgendem Beispiel wird eine Sequenz erzeugt, die alle Elemente im Array numbers enthält, die vor der 9 auftreten, da dies die erste Zahl in der Sequenz ist, die die Bedingung nicht erfüllt: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 folgendem Beispiel wird gezeigt, wie Sie mehrere Eingabeparameter angeben, indem Sie sie in Klammern einschließen.The following example specifies multiple input parameters by enclosing them in parentheses. Mit der Methode werden alle Elemente im numbers-Array zurückgegeben, bis eine Zahl erreicht wird, deren Wert kleiner ist als ihre Ordnungspostion im Array: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

Typrückschluss in LambdaausdrückenType inference in lambda expressions

Beim Schreiben von Lambdas müssen Sie oftmals keinen Typ für die Eingabeparameter angeben, da der Compiler den Typ auf der Grundlage des Lambdatexts, der Parametertypen und anderer Faktoren ableiten kann, wie in der C#-Programmiersprachenspezifikation beschrieben.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. Bei den meisten Standardabfrageoperatoren entspricht die erste Eingabe dem Typ der Elemente in der Quellsequenz.For most of the standard query operators, the first input is the type of the elements in the source sequence. Beim Abfragen von IEnumerable<Customer> wird die Eingabevariable als Customer-Objekt abgeleitet, sodass Sie auf die zugehörigen Methoden und Eigenschaften zugreifen können: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");

Die allgemeinen Regeln für Typrückschlüsse bei Lambdas lauten wie folgt:The general rules for type inference for lambdas are as follows:

  • Der Lambda-Ausdruck muss dieselbe Anzahl von Parametern enthalten wie der Delegattyp.The lambda must contain the same number of parameters as the delegate type.

  • Jeder Eingabeparameter im Lambda muss implizit in den entsprechenden Delegatparameter konvertiert werden können.Each input parameter in the lambda must be implicitly convertible to its corresponding delegate parameter.

  • Der Rückgabewert des Lambdas (falls vorhanden) muss implizit in den Rückgabetyp des Delegaten konvertiert werden können.The return value of the lambda (if any) must be implicitly convertible to the delegate's return type.

Beachten Sie, dass Lambdaausdrücke keinen eigenen Typ haben, da das allgemeine Typsystem kein internes Konzept von „Lambdaausdrücken“ aufweist.Note that lambda expressions in themselves don't have a type because the common type system has no intrinsic concept of "lambda expression." Es kann manchmal praktisch sein, informell vom „Typ“ eines Lambdaausdrucks zu sprechen.However, it's sometimes convenient to speak informally of the "type" of a lambda expression. In einem solchen Fall bezeichnet Typ den Delegattyp bzw. den Expression -Typ, in den der Lambda-Ausdruck konvertiert wird.In these cases the type refers to the delegate type or Expression type to which the lambda expression is converted.

Erfassen äußerer Variablen sowie des Variablenbereichs in LambdaausdrückenCapture of outer variables and variable scope in lambda expressions

Lambdas können auf äußere Variablen verweisen.Lambdas can refer to outer variables. Hierbei handelt es sich um die Variablen, die im Bereich der Methode, mit der der Lambdaausdruck definiert wird, oder im Bereich des Typs liegen, der den Lambdaausdruck enthält.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. Variablen, die auf diese Weise erfasst werden, werden zur Verwendung in Lambda-Ausdrücken gespeichert, auch wenn die Variablen andernfalls außerhalb des Gültigkeitsbereichs liegen und an die Garbage Collection übergeben würden.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. Eine äußere Variable muss definitiv zugewiesen sein, bevor sie in einem Lambda-Ausdruck verwendet werden kann.An outer variable must be definitely assigned before it can be consumed in a lambda expression. Das folgende Beispiel veranschaulicht diese Regeln: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
}

Die folgenden Regeln gelten für den Variablenbereich in Lambda-Ausdrücken:The following rules apply to variable scope in lambda expressions:

  • Eine erfasste Variable wird erst dann an die Garbage Collection übergeben, wenn der darauf verweisende Delegat für die Garbage Collection geeignet ist.A variable that is captured will not be garbage-collected until the delegate that references it becomes eligible for garbage collection.

  • Variablen, die in einem Lambdaausdruck eingeführt wurden, sind in der einschließenden Methode nicht sichtbar.Variables introduced within a lambda expression are not visible in the enclosing method.

  • Ein Lambdaausdruck kann einen in-, ref- oder out-Parameter nicht direkt von der einschließenden Methode erfassen.A lambda expression cannot directly capture an in, ref, or out parameter from the enclosing method.

  • Eine return-Anweisung in einem Lambdaausdruck bewirkt keine Rückgabe durch die einschließende Methode.A return statement in a lambda expression doesn't cause the enclosing method to return.

  • Ein Lambdaausdruck darf keine goto-, break- oder continue-Anweisung enthalten, wenn das Ziel dieser Sprunganweisung außerhalb des Lambdaausdrucksblocks liegt.A lambda expression cannot contain a goto, break, or continue statement if the target of that jump statement is outside the lambda expression block. Eine Sprunganweisung darf auch nicht außerhalb des Lambdaausdrucksblocks sein, wenn das Ziel im Block ist.It's also an error to have a jump statement outside the lambda expression block if the target is inside the block.

C#-SprachspezifikationC# language specification

Weitere Informationen finden Sie im Abschnitt Anonyme Funktionsausdrücke der C#-Sprachspezifikation.For more information, see the Anonymous function expressions section of the C# language specification.

Delegates, Events, and Lambda Expressions (Delegaten, Ereignisse und Lambda-Ausdrücke) in C# 3.0 Cookbook, Third Edition: More than 250 solutions for C# 3.0 programmers (C# 3.0-Cookbook, 3. Auflage: Mehr als 250 Lösungen für C# 3.0-Programmierer)Delegates, Events, and Lambda Expressions in C# 3.0 Cookbook, Third Edition: More than 250 solutions for C# 3.0 programmers

Siehe auchSee also