LINQ - Lambda表示式
術語“Lambda 表示式”來源於“lambda 演算”,後者是一種用於定義函式的數學記法。作為 LINQ 等式可執行部分的 Lambda 表示式,以執行時的方式轉換邏輯,以便可以方便地傳遞給資料來源。但是,Lambda 表示式的應用並不僅限於 LINQ。
這些表示式由以下語法表示:
(Input parameters) ⇒ Expression or statement block
這是一個 Lambda 表示式的示例:
y ⇒ y * y
上述表示式指定了一個名為 y 的引數,並且 y 的值被平方。但是,無法以這種形式執行 Lambda 表示式。下面顯示了 C# 中 Lambda 表示式的示例。
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace lambdaexample {
class Program {
delegate int del(int i);
static void Main(string[] args) {
del myDelegate = y ⇒ y * y;
int j = myDelegate(5);
Console.WriteLine(j);
Console.ReadLine();
}
}
}
VB
Module Module1
Private Delegate Function del(ByVal i As Integer) As Integer
Sub Main(ByVal args As String())
Dim myDelegate As del = Function(y) y * y
Dim j As Integer = myDelegate(5)
Console.WriteLine(j)
Console.ReadLine()
End Sub
End Module
當編譯並執行上述 C# 或 VB 程式碼時,將產生以下結果:
25
表示式 Lambda
由於上述 Lambda 表示式語法中的表示式位於右側,因此它們也稱為表示式 Lambda。
非同步 Lambda
透過使用 async 關鍵字加入非同步處理建立的 Lambda 表示式稱為非同步 Lambda。下面是一個非同步 Lambda 的示例。
Func<Task<string>> getWordAsync = async()⇒ “hello”;
標準查詢運算子中的 Lambda
查詢運算子中的 Lambda 表示式由同一運算子按需計算,並持續作用於輸入序列中的每個元素,而不是整個序列。Lambda 表示式允許開發人員將自己的邏輯輸入到標準查詢運算子中。在下面的示例中,開發人員使用了“Where”運算子,利用 Lambda 表示式從給定列表中提取奇數值。
C#
//Get the average of the odd Fibonacci numbers in the series...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace lambdaexample {
class Program {
static void Main(string[] args) {
int[] fibNum = { 1, 1, 2, 3, 5, 8, 13, 21, 34 };
double averageValue = fibNum.Where(num ⇒ num % 2 == 1).Average();
Console.WriteLine(averageValue);
Console.ReadLine();
}
}
}
VB
Module Module1
Sub Main()
Dim fibNum As Integer() = {1, 1, 2, 3, 5, 8, 13, 21, 34}
Dim averageValue As Double = fibNum.Where(Function(num) num Mod 2 = 1).Average()
Console.WriteLine(averageValue)
Console.ReadLine()
End Sub
End Module
當編譯並執行上述程式碼時,將產生以下結果:
7.33333333333333
Lambda 中的型別推斷
在 C# 中,型別推斷方便地用於各種情況下,而且無需顯式指定型別。但是,對於 Lambda 表示式,型別推斷僅在每個型別都已指定的情況下才有效,因為必須滿足編譯器的要求。讓我們考慮以下示例。
delegate int Transformer (int i);
在這裡,編譯器使用型別推斷來推斷 x 是一個整數,這是透過檢查 Transformer 的引數型別來完成的。
Lambda 表示式中的變數作用域
在 Lambda 表示式中使用變數作用域時,有一些規則,例如,在 Lambda 表示式中初始化的變數不能在外部方法中可見。還有一個規則是,除非引用它的委託有資格進行垃圾回收,否則捕獲的變數不會被垃圾回收。此外,還有一條規則禁止 Lambda 表示式中的 return 語句導致封閉方法返回。
以下是一個演示 Lambda 表示式中變數作用域的示例。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace lambdaexample {
class Program {
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:
// The delegate has not been invoked yet.
Console.WriteLine("j = {0}", j); // Invoke the delegate.
bool boolResult = del();
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);
Console.WriteLine(result);
Console.ReadKey();
}
}
}
}
當編譯並執行上述程式碼時,將產生以下結果:
j = 0 j = 10. b = True True
表示式樹
Lambda 表示式廣泛用於表示式樹的構建。表示式樹將程式碼轉換為類似於樹的資料結構,其中每個節點本身都是一個表示式,例如方法呼叫或二元運算子,例如 x<y。以下是使用 Lambda 表示式構建表示式樹的示例。
語句 Lambda
還存在語句 Lambda,它包含兩到三個語句,但不用於構建表示式樹。語句 Lambda 必須編寫 return 語句。
語句 Lambda 的語法
(params)⇒ {statements}
語句 Lambda 的示例
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
namespace lambdaexample {
class Program {
static void Main(string[] args) {
int[] source = new[] { 3, 8, 4, 6, 1, 7, 9, 2, 4, 8 };
foreach (int i in source.Where(x ⇒
{
if (x <= 3)
return true;
else if (x >= 7)
return true;
return false;
}
))
Console.WriteLine(i);
Console.ReadLine();
}
}
}
當編譯並執行上述程式碼時,將產生以下結果:
3 8 1 7 9 2 8
Lambda 用作基於方法的 LINQ 查詢中的引數,並且不允許像匿名方法一樣位於運算子(如is或as)的左側。儘管 Lambda 表示式與匿名方法非常相似,但它們並不侷限於僅用作委託。
使用 Lambda 表示式時需要注意的幾點
Lambda 表示式可以返回值,並且可以具有引數。
可以使用多種方法定義 Lambda 表示式的引數。
如果 Lambda 表示式中只有一個語句,則不需要花括號;如果有多個語句,則必須編寫花括號和返回值。
使用 Lambda 表示式,可以透過稱為閉包的功能訪問 Lambda 表示式塊外部的變數。應謹慎使用閉包以避免任何問題。
不可能在任何 Lambda 表示式內執行任何不安全程式碼。
Lambda 表示式不能用於運算子的左側。