LambdaausdrückeLambda expressions

Ein Lambdaausdruck ist ein Codeblock (bzw. ein Ausdrucks- oder ein Anweisungsblock), der wie ein Objekt behandelt wird.A lambda expression is a block of code (an expression or a statement block) that is treated as an object. Er kann als Argument an eine Methode übergeben werden, und er kann auch von Methodenaufrufen zurückgegeben werden.It can be passed as an argument to methods, and it can also be returned by method calls. Lambdaausdrücke finden bei folgenden Aktionen Anwendung:Lambda expressions are used extensively for:

Lambdaausdrücke sind Code, der entweder als Delegat oder als eine Ausdrucksbaumstruktur repräsentiert werden kann, die an einen Delegat kompiliert.Lambda expressions are code that can be represented either as a delegate, or as an expression tree that compiles to a delegate. Der genaue Delegattyp eines Lambdaausdrucks hängt von dessen Parametern und Rückgabewert ab.The specific delegate type of a lambda expression depends on its parameters and return value. Lambdaausdrücke, die keinen Wert zurückgeben, korrespondieren mit einem bestimmten Action-Delegat, abhängig von der Anzahl seiner Parameter.Lambda expressions that don't return a value correspond to a specific Action delegate, depending on its number of parameters. Lambdaausdrücke, die keinen Wert zurückgeben, entsprechen einem bestimmten Func-Delegat, abhängig von der Anzahl seiner Parameter.Lambda expressions that return a value correspond to a specific Func delegate, depending on its number of parameters. Beispielsweise entspricht ein Lambda-Ausdruck, der über zwei Parameter verfügt, aber keinen Wert zurückgibt ein Action<T1,T2> delegieren.For example, a lambda expression that has two parameters but returns no value corresponds to an Action<T1,T2> delegate. Ein Lambda-Ausdruck, der über einen Parameter verfügt und einen Wert zurückgibt entspricht Func<T,TResult> delegieren.A lambda expression that has one parameter and returns a value corresponds to Func<T,TResult> delegate.

Ein Lambdaausdruck verwendet =>, den Lambdadeklarationsoperator, um die Parameterliste des Lambdas von dessen ausführbarem Code zu trennen.A lambda expression uses =>, the lambda declaration operator, to separate the lambda's parameter list from its executable code. Zum Erstellen eines Lambdaausdrucks geben Sie Eingabeparameter (falls vorhanden) auf der linken Seite des Lambdaoperators an und stellen den Ausdrucks- oder Anweisungsblock auf die andere Seite.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. Beispielsweise gibt der Einzelzeilen-Lambdaausdruck x => x * x einen Parameter an, der x heißt und das Quadrat des Werts von x zurückgibt.For example, the single-line lambda expression x => x * x specifies a parameter that’s named x and returns the value of x squared. Dieser Ausdruck kann einem Delegattyp zuwiesen werden, wie im folgenden Beispiel dargestellt:You can assign this expression to a delegate type, as the following example shows:

using System;

class Example
{
   public static void Main()
   {
      Func<int, int> square = x => x * x; 
      Console.WriteLine(square(25));
   }
}
// The example displays the following output:
//      625

Sie können ihn aber auch als Methodenargument übergeben:Or you can pass it directly as a method argument:

using System;

public class Example
{
   static void Main()  
   {  
      ShowValue(x => x * x);  
   }  

   private static void ShowValue(Func<int,int> op)
   {
      for (int ctr = 1; ctr <= 5; ctr++)
         Console.WriteLine("{0} x {0} = {1}",
                           ctr,  op(ctr));
   }
}
// The example displays the following output:
//   1, 1 x 1 = 1
//   2, 2 x 2 = 4
//   3, 3 x 3 = 9
//   4, 4 x 4 = 16
//   5, 5 x 5 = 25

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> testEquality = (x, y) => x == y;  // test for equality

Für gewöhnlich verwendet der Compiler den Typrückschluss, um Parametertypen zu ermitteln.Ordinarily, the compiler uses type inference in determining parameter types. Für den Compiler kann es jedoch schwierig oder unmöglich sein, die Eingabetypen abzuleiten.However, sometimes it is difficult or impossible for the compiler to infer the input types. Wenn dieses Problem auftritt, können Sie die Typen explizit angeben, wie im folgenden Beispiel dargestellt:When this occurs, you can specify the types explicitly, as in the following example:

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

Beachten Sie im vorherigen Beispiel, dass der Text eines Ausdruckslambdas ein Methodenaufruf sein kann.Note in the previous example that the body of an expression lambda can consist of a method call. Wenn Sie allerdings Ausdrucksbaumstrukturen erstellen, die außerhalb des .NET Frameworks ausgewertet werden, wie z.B. in SQL Server oder Entity Framework (EF), sollten Sie davon absehen, Methodenaufrufe in Lambdaausdrücken zu verwenden, da die Methoden möglicherweise außerhalb des Kontexts der .NET-Implementierung bedeutungslos sind.However, if you are creating expression trees that are evaluated outside of the .NET Framework, such as in SQL Server or Entity Framework (EF), you should refrain from using method calls in lambda expressions, since the methods may have no meaning outside the context of the .NET implementation. Falls Sie dennoch Methodenaufrufe verwenden möchten, prüfen Sie diese gründlich, um sicherzustellen, dass die Methodenaufrufe erfolgreich aufgelöst werden können.If you do choose to use method calls in this case, be sure to test them thoroughly to ensure that the method calls can be successfuly resolved.

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) => { statement; }

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.

using System;


public class Example
{
    delegate void TestDelegate(string s);

    public static void Main()
    {
       TestDelegate test = n => { string s = n + " " + "World"; Console.WriteLine(s); };  
       test("Hello");
    }
}
// The example displays the following output:
//     Hello World

Anweisungslambdas, wie anonyme Methoden, können nicht zum Erstellen von Ausdrucksbaumstrukturen verwendet werden.Statement lambdas, like anonymous methods, cannot be used to create expression trees.

Asynchrone LambdasAsync lambdas

Sie können mit den Schlüsselwörtern async und await Lambdaausdrü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 Beispiel ruft beispielsweise eine ShowSquares-Methode auf, die asynchron ausgeführt wird.For example, the example calls a ShowSquares method that executes asynchronously.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      Begin().Wait();
   }

   private static async Task Begin()
   {
      for (int ctr = 2; ctr <= 5; ctr++) {
         var result = await ShowSquares(ctr);
         Console.WriteLine("{0} * {0} = {1}", ctr, result);
      }
   }

   private static async Task<int>  ShowSquares(int number)
   {
         return await Task.Factory.StartNew( x => (int)x * (int)x, number);
   } 
}

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 integrierten Support 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 fünf Komponenten verwenden, um eine Zahlensequenz an einen Lambdaausdruck zu übergeben; dadurch wird jeder Wert verdoppelt, und es wird ein Tupel mit fünf Komponenten zurückgegeben, das das Ergebnis der Multiplikation enthält.The following example uses tuple with 5 components to pass a sequence of numbers to a lambda expression, which doubles each value and returns a tuple with 5 components that contains the result of the multiplications.

using System;

public class Example
{
    public static void Main()
    {
        var numbers = (2, 3, 4, 5, 6);
        Func<(int, int, int, int, int), (int, int, int, int, int)> doubleThem = (n) => (n.Item1 * 2, n.Item2 * 2, n.Item3 * 2, n.Item4 * 2, n.Item5 * 2);
        var doubledNumbers = doubleThem(numbers);

        Console.WriteLine("The set {0} doubled: {1}", numbers, doubledNumbers);
        Console.ReadLine();
    }
}
// The example displays the following output:
//    The set (2, 3, 4, 5, 6) doubled: (4, 6, 8, 10, 12)

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.

using System;

public class Example
{
    public static void Main()
    {
        var numbers = (2, 3, 4, 5, 6);
        Func<(int n1, int n2, int n3, int n4, int n5), (int, int, int, int, int)> doubleThem = (n) => (n.n1 * 2, n2 * 2, n.n3 * 2, n.n4 * 2, n.n5 * 2);
        var doubledNumbers = doubleThem(numbers);

        Console.WriteLine("The set {0} doubled: {1}", numbers, doubledNumbers);
        Console.ReadLine();
    }
}
// The example displays the following output:
//    The set (2, 3, 4, 5, 6) doubled: (4, 6, 8, 10, 12)

Weitere Informationen zum Support für Tupel in C# finden Sie unter C# Tuple types (C#-Tupeltypen).For more information on support for tuples in C#, see C# Tuple types.

Lambdas mit StandardabfrageoperatorenLambdas with the standard query operators

LINQ, u. a. die Objekte in anderen Implementierungen weisen einen Eingabeparameter, die, deren Typ wird, von der Func<TResult> -Familie generischer Delegaten.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 beispielsweise die Func<TResult> Delegaten, deren Syntax lautet:For example, consider the Func<TResult> delegate, whose syntax is:

public delegate TResult Func<TArg, TResult>(TArg arg);

Der Delegat kann mit Code wie dem folgenden instanziiert werdenThe delegate can be instantiated with code like the following

Func<int, bool> func = (x) => x == 5; 

Hier ist int ein Eingabeparameter, und bool ist der Rückgabewert.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. Wenn der folgende Func-Delegat aufgerufen wird, gibt er TRUE oder FALSE zurück, um anzugeben, ob der Eingabeparameter gleich 5 ist:When the following Func delegate is invoked, it returns true or false to indicate whether the input parameter is equal to 5:

Console.WriteLine(func(4));      // Returns "False".     

Sie können auch einen Lambda-Ausdruck angeben, wenn der Argumenttyp ist ein Expression<TDelegate>, z. B. in den Standardabfrageoperatoren, die in definiert werden die Queryable Typ.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. Geben Sie bei einer Expression<TDelegate> Argument der Lambda-Ausdruck in eine Ausdrucksbaumstruktur kompiliert wird.When you specify an Expression<TDelegate> argument, the lambda is compiled to an expression tree. In folgendem Beispiel wird der Standardabfrageoperator System.Linq.Enumerable.Count verwendet.The following example uses the System.Linq.Enumerable.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 {0} odd numbers in the set", oddNumbers);
// Output: There are 5 odd numbers in the set

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 Lambdaausdruck zählt die Ganzzahlen (n), bei denen nach dem Dividieren durch zwei als Rest 1 bleibt.This particular lambda expression counts those integers (n) that, 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.

var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);
foreach (var number in firstNumbersLessThan6)
   Console.Write("{0}     ", number);  
// 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 Zahlenarray zurückgegeben, bis eine Zahl erreicht wird, deren Wert kleiner ist als seine 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.

 var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);
foreach (var number in firstSmallNumbers)
   Console.Write("{0}     ", number);
 // 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, des zugrunde liegenden Typs des Parameters und anderer Faktoren per Rückschluss ableiten kann, wie in der C#-Programmiersprachenspezifikation beschrieben.When writing lambdas, you often do not 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:

  • 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.

  • Jedes Eingabeargument im Lambda muss implizit in den entsprechenden Delegatparameter konvertierbar sein.Each input argument 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 Lambda-Ausdrücke keinen eigenen Typ haben, da das allgemeine Typsystem kein internes Konzept von "Lambda-Ausdrücken" aufweist.Note that lambda expressions in themselves do not have a type because the common type system has no intrinsic concept of "lambda expression." Es kann manchmal praktisch sein, informell vom "Typ" eines Lambda-Ausdrucks zu sprechen.However, it is 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.

Variablenbereich in Lambda-AusdrückenVariable Scope in Lambda Expressions

Lambdas können auf äußere Variablen verweisen (siehe Anonyme Methoden), die im Bereich der Methode, mit der die Lambdafunktion definiert wird, oder im Bereich des Typs liegen, der den Lambdaausdruck enthält.Lambdas can refer to outer variables (see Anonymous methods) that are in scope in the method that defines the lambda function, 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.

using System;

delegate bool D();  
delegate bool D2(int i);  
  
class Test  
{  
    D del;  
    D2 del2;  
    public void TestMethod(int input)  
    {  
        int j = 0;  
        // Initialize the delegates with lambda expressions.  
        // Note access to 2 outer variables.  
        // del will be invoked within this method.  
        del = () => { j = 10;  return j > input; };  
  
        // del2 will be invoked after TestMethod goes out of scope.  
        del2 = (x) => {return x == j; };  
  
        // Demonstrate value of j:  
        // Output: j = 0   
        // The delegate has not been invoked yet.  
        Console.WriteLine("j = {0}", j);        // Invoke the delegate.  
        bool boolResult = del();  
  
        // Output: j = 10 b = True  
        Console.WriteLine("j = {0}. b = {1}", j, boolResult);  
    }  
  
    static void Main()  
    {  
        Test test = new Test();  
        test.TestMethod(5);  
  
        // Prove that del2 still has a copy of  
        // local variable j from TestMethod.  
        bool result = test.del2(10);  
  
        // Output: True  
        Console.WriteLine(result);  
    }  
}  
// The example displays the following output:
//      j = 0
//      j = 10. b = True
//      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 Lambda-Ausdruck eingeführt wurden, sind in der äußeren Methode nicht sichtbar.Variables introduced within a lambda expression are not visible in the outer method.

  • Ein Lambda-Ausdruck kann einen ref - oder out -Parameter nicht direkt von einer einschließenden Methode erfassen.A lambda expression cannot directly capture a ref or out parameter from an enclosing method.

  • Eine return-Anweisung in einem Lambda-Ausdruck bewirkt keine Rückgabe durch die einschließende Methode.A return statement in a lambda expression does not cause the enclosing method to return.

  • Ein Lambda-Ausdruck kann eine goto -Anweisung, break -Anweisung oder continue -Anweisung enthalten, die innerhalb der Lambda-Funktion liegt, wenn das Ziel der jump-Anweisung außerhalb des Blocks liegt.A lambda expression cannot contain a goto statement, break statement, or continue statement that is inside the lambda function if the jump statement’s target is outside the block. Eine jump-Anweisung darf auch nicht außerhalb des Lambda-Funktionsblocks sein, wenn das Ziel im Block ist.It is also an error to have a jump statement outside the lambda function block if the target is inside the block.

Siehe auchSee also

LINQ (Language Integrated Query) LINQ (Language-Integrated Query)
Anonyme Methoden Anonymous methods
AusdrucksbaumstrukturenExpression trees