# Lambda 運算式 (c # 參考) Lambda expressions (C# reference)

「Lambda 運算式」** 是下列兩種形式之一的運算式：A lambda expression is an expression of any of the following two forms:

• 以運算式作為主體的運算式 LambdaExpression lambda that has an expression as its body:

(input-parameters) => expression

• 以陳述式區塊作為主體的陳述式 LambdaStatement lambda that has a statement block as its body:

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


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


System.Linq.Expressions.Expression<Func<int, int>> e = x => x * x;
Console.WriteLine(e);
// Output:
// x => (x * x)


int[] numbers = { 2, 3, 4, 5 };
var squaredNumbers = numbers.Select(x => x * x);
Console.WriteLine(string.Join(" ", squaredNumbers));
// Output:
// 4 9 16 25


## 運算式 LambdaExpression lambdas

=> 運算子右邊有運算式的 Lambda 運算式稱為「運算式 Lambda」**。A lambda expression with an expression on the right side of the => operator is called an expression lambda. 運算式 Lambda 會傳回運算式的結果，並採用下列基本形式：An expression lambda returns the result of the expression and takes the following basic form:

(input-parameters) => expression


## 陳述式 LambdaStatement lambdas

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


Action<string> greet = name =>
{
string greeting = $"Hello {name}!"; Console.WriteLine(greeting); }; greet("World"); // Output: // Hello World!  您無法使用語句 lambda 來建立運算式樹狀架構。You cannot use statement lambdas to create expression trees. ## Lambda 運算式的輸入參數Input parameters of a lambda expression 您可以用括弧括住 lambda 運算式的輸入參數。You enclose input parameters of a lambda expression in parentheses. 以空括號指定零個輸入參數：Specify zero input parameters with empty parentheses: Action line = () => Console.WriteLine();  如果 lambda 運算式只有一個輸入參數，則括弧是選擇性的：If a lambda expression has only one input parameter, parentheses are optional: Func<double, double> cube = x => x * x * x;  兩個或多個輸入參數會以逗號分隔：Two or more input parameters are separated by commas: Func<int, int, bool> testForEquality = (x, y) => x == y;  有時編譯器無法推斷輸入參數的類型。Sometimes the compiler can't infer the types of input parameters. 您可以明確指定類型，如下列範例所示：You can specify the types explicitly as shown in the following example: Func<int, string, bool> isTooLong = (int x, string s) => s.Length > x;  輸入參數類型必須全部為明確或全部為隱含；否則，會發生 CS0748 編譯器錯誤。Input parameter types must be all explicit or all implicit; otherwise, a CS0748 compiler error occurs. 從 c # 9.0 開始，您可以使用 [ 捨棄 ] 來指定運算式中未使用之 lambda 運算式的兩個或多個輸入參數：Beginning with C# 9.0, you can use discards to specify two or more input parameters of a lambda expression that aren't used in the expression: Func<int, int, int> constant = (_, _) => 42;  當您使用 lambda 運算式來 提供事件處理常式時，lambda 捨棄參數可能很有用。Lambda discard parameters may be useful when you use a lambda expression to provide an event handler. 注意 為了回溯相容性，如果只命名單一輸入參數 _ ，則會將 lambda 運算式內的 _ 視為該參數的名稱。For backwards compatibility, if only a single input parameter is named _, then, within a lambda expression, _ is treated as the name of that parameter. ## 非同步 LambdaAsync lambdas 您可以使用 asyncawait 關鍵字，輕鬆建立結合非同步處理的 Lambda 運算式和陳述式。You can easily create lambda expressions and statements that incorporate asynchronous processing by using the async and await keywords. 例如，下列 Windows Form 範例包含呼叫並等候非同步方法 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); } }  您可以使用非同步 Lambda 加入相同的事件處理常式。You can add the same event handler by using an async lambda. 若要加入這個處理常式，請將 async 修飾詞加入至 Lambda 參數清單前面，如下列範例所示：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); } }  如需如何建立和使用非同步方法的詳細資訊，請參閱使用 Async 和 Await 進行非同步程式設計For more information about how to create and use async methods, see Asynchronous Programming with async and await. ## Lambda 運算式和元組Lambda expressions and tuples 從 c # 7.0 開始，c # 語言提供 元組的內建支援。Starting with C# 7.0, the C# language provides built-in support for tuples. 您可以將元組當做引數提供給 Lambda 運算式，而您的 Lambda 運算式也可以傳回元組。You can provide a tuple as an argument to a lambda expression, and your lambda expression can also return a tuple. 在某些情況下，C# 編譯器會使用型別推斷來判斷元組元件的類型。In some cases, the C# compiler uses type inference to determine the types of tuple components. 若要定義元組，請以括號括住其元件的逗號分隔清單。You define a tuple by enclosing a comma-delimited list of its components in parentheses. 下列範例使用具有 3 個元件的元組將一連串數字傳遞至 Lambda 運算式，這會使每個值加倍，並傳回具有三個元件的元組，其中包含乘法運算的結果。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)


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}");  如需 c # 元組的詳細資訊，請參閱 元組類型For more information about C# tuples, see Tuple types. ## 具有標準查詢運算子的 LambdaLambdas with the standard query operators 其他實作中的 LINQ to Objects 具有輸入參數，其類型是其中一種 Func<TResult> 系列的泛型委派。LINQ to Objects, among other implementations, have an input parameter whose type is one of the Func<TResult> family of generic delegates. 這些委派使用型別參數定義輸入參數的數目和類型，以及委派的傳回型別。These delegates use type parameters to define the number and type of input parameters, and the return type of the delegate. 對於封裝套用至一組來源資料中每個項目的使用者定義運算式而言，Func 委派非常實用。Func delegates are very useful for encapsulating user-defined expressions that are applied to each element in a set of source data. 例如，請考慮 Func<T,TResult> 委派類型：For example, consider the Func<T,TResult> delegate type: public delegate TResult Func<in T, out TResult>(T arg)  委派可以具現化為 Func<int, bool> 執行個體，其中 int 是輸入參數，而 bool 是傳回值。The delegate can be instantiated as a Func<int, bool> instance where int is an input parameter and bool is the return value. 傳回值一律在最後一個類型參數中指定。The return value is always specified in the last type parameter. 例如，Func<int, string, bool> 定義具有兩個輸入參數 (intstring) 的委派與 bool 的傳回類型。For example, Func<int, string, bool> defines a delegate with two input parameters, int and string, and a return type of bool. 叫用下列 Func 委派時會傳回布林值，指出輸入參數是否等於 5：The following Func delegate, when it's invoked, returns Boolean value that indicates whether the input parameter is equal to five: Func<int, bool> equalsFive = x => x == 5; bool result = equalsFive(4); Console.WriteLine(result); // False  您也可以在引數類型為 Expression<TDelegate> 時提供 Lambda 運算式，例如在定義於 Queryable 類型的標準查詢運算子中。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. 當您指定 Expression<TDelegate> 引數時，Lambda 會編譯成運算式樹狀結構。When you specify an Expression<TDelegate> argument, the lambda is compiled to an expression tree. 下列範例使用 Count 標準查詢運算子：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)}");


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


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


## Lambda 運算式中的型別推斷Type inference in lambda expressions

customers.Where(c => c.City == "London");


Lambda 型別推斷的一般規則如下所示：The general rules for type inference for lambdas are as follows:

• Lambda 必須包含與委派類型相同數目的參數。The lambda must contain the same number of parameters as the delegate type.

• Lambda 中的每個輸入參數都必須能夠隱含轉換為其對應的委派參數。Each input parameter in the lambda must be implicitly convertible to its corresponding delegate parameter.

• Lambda 的傳回值 (如果有的話) 必須能夠隱含轉換為委派的傳回類型。The return value of the lambda (if any) must be implicitly convertible to the delegate's return type.

## Lambda 運算式中的擷取外部變數和變數範圍Capture of outer variables and variable scope in lambda expressions

Lambda 可以參考「外部變數」**。Lambdas can refer to outer variables. 這些是在定義 Lambda 運算式的方法範圍內，或是在包含 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. 以這種方式擷取的變數會加以儲存，以便在 Lambda 運算式中使用，即使這些變數可能會超出範圍而遭到記憶體回收。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. 外部變數必須確實指派，才能用於 Lambda 運算式。An outer variable must be definitely assigned before it can be consumed in a lambda expression. 下列範例將示範這些規則：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
}


• 已擷取的變數要等到參考該變數的委派符合記憶體回收的資格時，才會進行記憶體回收。A variable that is captured will not be garbage-collected until the delegate that references it becomes eligible for garbage collection.

• 導入 Lambda 運算式內的變數無法在封入方法中看見。Variables introduced within a lambda expression are not visible in the enclosing method.

• Lambda 運算式無法直接從封入方法擷取 inrefout 參數。A lambda expression cannot directly capture an in, ref, or out parameter from the enclosing method.

• Lambda 運算式中的 return 陳述式不會造成封入方法傳回。A return statement in a lambda expression doesn't cause the enclosing method to return.

• 如果該跳躍陳述式的目標位於 Lambda 運算式區塊之外，則 Lambda 運算式不能包含 gotobreakcontinue 陳述式。A lambda expression cannot contain a goto, break, or continue statement if the target of that jump statement is outside the lambda expression block. 即使目標位於區塊內，跳躍陳述式出現在 Lambda 運算式區塊外部也一樣是錯誤。It's also an error to have a jump statement outside the lambda expression block if the target is inside the block.

Func<double, double> square = static x => x * x;