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

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. Ein Lambdaausdruck, der beispielsweise zwei Parameter hat, aber keinen Wert zurückgibt, entspricht einem Action<T1,T2>-Delegat.For example, a lambda expression that has two parameters but returns no value corresponds to an Action<T1,T2> delegate. Ein Lambdaausdruck, der beispielsweise zwei Parameter hat, aber keinen Wert zurückgibt, entspricht einem Func<T,TResult>-Delegat.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:

Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
// Output:
// 25

Sie können auch einen Lambdaausdruck einer Ausdrucksbaumstruktur zuweisen: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)

Sie können ihn aber auch als Methodenargument übergeben: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

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 (as you do in LINQ to Objects and LINQ to XML) the parameter is a delegate type System.Func<T,TResult>. Ein Lambda-Ausdruck ist die einfachste Möglichkeit zum Erstellen dieses Delegaten.A lambda expression is the most convenient way to create that delegate. 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 (as you do in LINQ to SQL) the parameter type is an expression tree type Expression<Func<TSource,TResult>>. Wie gesagt, ein Lambda-Ausdruck ist eine sehr präzise Methode, diese Ausdrucksbaumstruktur zu erstellen.Again, a lambda expression is just a very concise way to construct that expression tree. Durch die Lambdas können die Select -Aufrufe ähnlich aussehen, obwohl sich der mithilfe des Lambdas erstellte Objekttyp unterscheidet.The lambdas allow the Select calls to look similar although in fact the type of object created from the lambda is different.

Alle Einschränkungen für anonyme Methoden gelten auch für Lambdaausdrücke.All restrictions that apply to anonymous methods also apply to lambda expressions.

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) => { 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.

Action<string> greet = name => 
{ 
    string greeting = $"Hello {name}!";
    Console.WriteLine(greeting);
};
greet("World");
// 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 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.

Variablenbereich in LambdaausdrückenVariable scope in lambda expressions

Lambdas können auf äußere Variablen verweisen (siehe Anonyme Methoden), die im Bereich der Methode, mit der der Lambdaausdruck 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 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