Lambda 表達式是一種可用于建立
委托或
表達式目錄樹類型的
匿名函數。
通過使用 lambda 表達式,可以寫入可作為參數傳遞或作為函數調用值傳回的本地函數。
Lambda 表達式對于編寫 LINQ 查詢表達式特别有用。
若要建立 Lambda 表達式,需要在 Lambda 運算符 => 左側指定輸入參數(如果有),然後在另一側輸入表達式或語句塊。 例如,lambda 表達式x => x * x 指定名為x 的參數并傳回 x 如下面的示例所示,你可以将此表達式配置設定給委托類型:
C#
delegate int del(int i);
static void Main(string[] args)
{
del myDelegate = x => x * x;
int j = myDelegate(5); //j = 25
若要建立表達式目錄樹類型:
C#
using System.Linq.Expressions;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Expression<del> myET = x => x * x;
}
}
}
=> 運算符具有與指派運算符 (=) 相同的優先級并且是右結合運算(參見“運算符”文章的“結合性”部分)。
Lambda 在基于方法的 LINQ 查詢中用作标準查詢運算符方法(如 Where)的參數。
使用基于方法的文法在 Enumerable 類中調用Where 方法時(如在 LINQ to Objects 和 LINQ to XML 中一樣),參數是委托類型System.Func<T, TResult>。使用 Lambda 表達式建立該委托最為友善。 例如,當你在 System.Linq.Queryable 類中調用相同的方法時(如在 LINQ to SQL 中一樣),參數類型為System.Linq.Expressions.Expression<Func>,其中 Func 是最多具有十六個輸入參數的任何一個 Func 委托。同樣,Lambda 表達式隻是一種非常簡潔的構造該表達式目錄樹的方式。盡管事實上通過 Lambda 建立的對象具有不同的類型,但 Lambda 使得 Where 調用看起來類似。
在上一個示例中,請注意委托簽名具有一個 int 類型的隐式類型輸入參數,并傳回int。可以将 Lambda 表達式轉換為該類型的委托,因為該表達式也具有一個輸入參數 (x),以及一個編譯器可隐式轉換為int
在 is 或 as 運算符的左側不允許使用 Lambda。
适用于匿名方法的所有限制也适用于 Lambda 表達式。 有關更多資訊,請參見匿名方法(C# 程式設計指南)。
表達式 lambda
表達式位于 => 運算符右側的 lambda 表達式稱為“表達式 lambda”。 表達式 lambda 廣泛用于表達式樹(C# 和 Visual Basic)的構造。表達式 lambda 會傳回表達式的結果,并采用以下基本形式:
(input parameters) => expression
僅當 lambda 隻有一個輸入參數時,括号才是可選的;否則括号是必需的。 括号内的兩個或更多輸入參數使用逗号加以分隔:
C#
(x, y) => x == y
有時,編譯器難以或無法推斷輸入類型。 如果出現這種情況,你可以按以下示例中所示方式顯式指定類型:
C#
(int x, string
使用空括号指定零個輸入參數:
C#
() => SomeMethod()
在上一個示例中,請注意表達式 Lambda 的主體可以包含一個方法調用。 但是,如果要建立在 .NET Framework 之外計算的表達式目錄樹(例如,在 SQL Server 中),則不應在 lambda 表達式中使用方法調用。在 .NET 公共語言運作時上下文之外,方法将沒有任何意義。
語句 lambda
語句 lambda 與表達式 lambda 表達式類似,隻是語句括在大括号中:
複制
(input parameters) => {statement;}
語句 lambda 的主體可以包含任意數量的語句;但是,實際上通常不會多于兩個或三個。
C#
delegate void TestDelegate(string s);
…
TestDelegate myDel = n => { string s = n + " " + "World"; Console.WriteLine(s); };
myDel("Hello");
像匿名方法一樣,語句 lambda 也不能用于建立表達式目錄樹。
異步 lambda
通過使用 async 和 await 關鍵字,你可以輕松建立包含異步處理的 lambda 表達式和語句。 例如,下面的 Windows 窗體示例包含一個調用和等待異步方法ExampleMethodAsync
C#
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
// ExampleMethodAsync returns a Task.
await ExampleMethodAsync();
textBox1.Text += "\r\nControl returned to Click event handler.\r\n";
}
async Task ExampleMethodAsync()
{
// The following line simulates a task-returning asynchronous process.
await
你可以使用異步 lambda 添加同一事件處理程式。 async
C#
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button1.Click += async (sender, e) =>
{
// ExampleMethodAsync returns a Task.
await ExampleMethodAsync();
textBox1.Text += "\r\nControl returned to Click event handler.\r\n";
};
}
async Task ExampleMethodAsync()
{
// The following line simulates a task-returning asynchronous process.
await
有關如何建立和使用異步方法的更多資訊,請參見使用 Async 和 Await 的異步程式設計(C# 和 Visual Basic)。
帶有标準查詢運算符的 lambda
許多标準查詢運算符都具有輸入參數,其類型是泛型委托系列 Func<T, TResult> 中的一種。這些委托使用類型參數來定義輸入參數的數量和類型,以及委托的傳回類型。Func 委托對于封裝使用者定義的表達式非常有用,這些表達式将應用于一組源資料中的每個元素。例如,請考慮以下委托類型:
C#
public delegate
可以将委托執行個體化為 Func<int,bool> myFunc,其中int 是輸入參數,bool傳回值始終在最後一個類型參數中指定。Func<int, string, bool> 定義包含兩個輸入參數(int 和string)且傳回類型為bool當調用下面的Func 委托時,該委托将傳回 true 或 false 以訓示輸入參數是否等于 5:
C#
Func<int, bool> myFunc = x => x == 5;
bool result = myFunc(4); // returns false of course
當參數類型為 Expression<Func> 時,你也可以提供 Lambda 表達式,例如在 System.Linq.Queryable 内定義的标準查詢運算符中。如果指定Expression<Func> 參數,lambda 将編譯為表達式目錄樹。
此處顯示了一個标準查詢運算符,Count 方法:
C#
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int
編譯器可以推斷輸入參數的類型,或者你也可以顯式指定該類型。 n)。
下面一行代碼将生成一個序列,其中包含 numbers
C#
var
此示例展示了如何通過将輸入參數括在括号中來指定多個輸入參數。 該方法将傳回數字數組中的所有元素,直至遇到一個值小于其位置的數字為止。 不要将 lambda 運算符 (=>) 與大于等于運算符 (>=) 混淆。
C#
var
Lambda 中的類型推理
在編寫 lambda 時,通常不必為輸入參數指定類型,因為編譯器可以根據 lambda 主體、參數的委托類型以及 C# 語言規範中描述的其他因素來推斷類型。對于大多數标準查詢運算符,第一個輸入是源序列中的元素類型。是以,如果要查詢 IEnumerable<Customer>,則輸入變量将被推斷為Customer
C#
customers.Where(c => c.City == "London");
Lambda 的一般規則如下:
- Lambda 包含的參數數量必須與委托類型包含的參數數量相同。
- Lambda 中的每個輸入參數必須都能夠隐式轉換為其對應的委托參數。
- Lambda 的傳回值(如果有)必須能夠隐式轉換為委托的傳回類型。
請注意,lambda 表達式本身沒有類型,因為正常類型系統沒有“Lambda 表達式”這一内部概念。但是,有時以一種非正式的方式談論 lambda 表達式的“類型”會很友善。在這些情況下,類型是指委托類型或 lambda 表達式所轉換到的Expression 類型。
Lambda 表達式中的變量範圍
在定義 lambda 函數的方法内或包含 lambda 表達式的類型内,lambda 可以引用範圍内的外部變量(參見匿名方法(C# 程式設計指南))。以這種方式捕獲的變量将進行存儲以備在 lambda 表達式中使用,即使在其他情況下,這些變量将超出範圍并進行垃圾回收。必須明确地配置設定外部變量,然後才能在 lambda 表達式中使用該變量。下面的示例示範這些規則:
C#
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
下列規則适用于 lambda 表達式中的變量範圍:
- 捕獲的變量将不會被作為垃圾回收,直至引用變量的委托符合垃圾回收的條件。
- 在外部方法中看不到 lambda 表達式内引入的變量。
- Lambda 表達式無法從封閉方法中直接捕獲 ref 或out
- Lambda 表達式中的傳回語句不會導緻封閉方法傳回。
- 如果跳轉語句的目标在塊外部,則 lambda 表達式不能包含位于 lambda 函數内部的goto 語句、break 語句或continue同樣,如果目标在塊内部,則在 lambda 函數塊外部使用跳轉語句也是錯誤的。
--轉的另一篇介紹lambda的blog--
lambda簡介
lambda運算符:所有的lambda表達式都是用新的lambda運算符 " => ",可以叫他,“轉到”或者 “成為”。運算符将表達式分為兩部分,左邊指定輸入參數,右邊是lambda的主體。
lambda表達式:
1.一個參數:param=>expr
2.多個參數:(param-list)=>expr
上面這些東西,記着,下面我們開始應用并闡述lambda,讓你樂在其中。
lambda應用闡述
闡述這技術,我先上一個例子,然後再慢慢深入分析。例子如下:
namespace 闡述lambda
{
public class Person
{
public string Name { get; set; }
public int Age { get;set; }
}
class Program
{
public static List<Person> PersonsList()
{
List<Person> persons = new List<Person>();
for (int i = 0; i < 7; i++)
{
Person p = new Person() { Name = i + "兒子", Age = 8 - i, };
persons.Add(p);
}
return persons;
}
static void Main(string[] args)
{
List<Person> persons = PersonsList();
persons = persons.Where(p => p.Age > 6).ToList(); //所有Age>6的Person的集合
Person per = persons.SingleOrDefault(p => p.Age == 1); //Age=1的單個people類
persons = persons.Where(p => p.Name.Contains("兒子")).ToList(); //所有Name包含兒子的Person的集合
}
}
}
看了上面的例子,相信你能看出它确實是個甜棗,呵呵,下面我們來看下(p=>p.Age>6)這樣的表達式,到底是怎麼回事。。
首先我們看下委托
//委托 逛超市
delegate int GuangChaoshi(int a);
static void Main(string[] args)
{
GuangChaoshi gwl = JieZhang;
Console.WriteLine(gwl(10) + ""); //列印20,委托的應用
Console.ReadKey();
}
//結賬
public static int JieZhang(int a)
{
return a + 10;
}
再看表達式
//委托 逛超市
delegate int GuangChaoshi(int a);
static void Main(string[] args)
{
// GuangChaoshi gwl = JieZhang;
GuangChaoshi gwl = p => p + 10;
Console.WriteLine(gwl(10) + ""); //列印20,表達式的應用
Console.ReadKey();
}
委托跟表達式的兩段代碼,我們可以看出一些東東吧:其實表達式(p => p + 10;)中的 p 就代表委托方法中的參數,而表達式符号右邊的 p+10,就是委托方法中的傳回結果。 大俠繞道,小蝦了解下。
下面再上兩個稍微複雜點的了解了解。
1.多參數的
//委托 逛超市
delegate int GuangChaoshi(int a,int b);
static void Main(string[] args)
{
GuangChaoshi gwl = (p,z) => z-(p + 10);
Console.WriteLine(gwl(10,100) + ""); //列印80,z對應參數b,p對應參數a
Console.ReadKey();
}
2. lambda主體運算複雜
/// <summary>
/// 委托 逛超市
/// </summary>
/// <param name="a">花費</param>
/// <param name="b">付錢</param>
/// <returns>找零</returns>
delegate int GuangChaoshi(int a,int b);
static void Main(string[] args)
{
GuangChaoshi gwl = (p, z) =>
{
int zuidixiaofei = 10;
if (p < zuidixiaofei)
{
return 100;
}
else
{
return z - p - 10;
}
};
Console.WriteLine(gwl(10,100) + ""); //列印80,z對應參數b,p對應參數a
Console.ReadKey();
}
上面這些例子,好好了解下,下面我要介紹一個系統指定的 Fun<T>委托。
Func委托
T 是參數類型,這是一個泛型類型的委托,用起來很友善的。
先上例子
static void Main(string[] args)
{
Func<int, string> gwl = p => p + 10 + "--傳回類型為string";
Console.WriteLine(gwl(10) + ""); //列印‘20--傳回類型為string’,z對應參數b,p對應參數a
Console.ReadKey();
}
說明:我們可以看到,這裡的p為int 類型參數, 然而lambda主體傳回的是string類型的。
再上一個例子
static void Main(string[] args)
{
Func<int, int, bool> gwl = (p, j) =>
{
if (p + j == 10)
{
return true;
}
return false;
};
Console.WriteLine(gwl(5,5) + ""); //列印‘True’,z對應參數b,p對應參數a
Console.ReadKey();
}
說明:從這個例子,我們能看到,p為int類型,j為int類型,傳回值為bool類型。
看完上面兩個例子,相信大家應該明白啦Func<T>的用法:多個參數,前面的為委托方法的參數,最後一個參數,為委托方法的傳回類型。
lambda表達式樹動态建立方法
static void Main(string[] args)
{
//i*j+w*x
ParameterExpression a = Expression.Parameter(typeof(int),"i"); //建立一個表達式樹中的參數,作為一個節點,這裡是最下層的節點
ParameterExpression b = Expression.Parameter(typeof(int),"j");
BinaryExpression be = Expression.Multiply(a,b); //這裡i*j,生成表達式樹中的一個節點,比上面節點高一級
ParameterExpression c = Expression.Parameter(typeof(int), "w");
ParameterExpression d = Expression.Parameter(typeof(int), "x");
BinaryExpression be1 = Expression.Multiply(c, d);
BinaryExpression su = Expression.Add(be,be1); //運算兩個中級節點,産生終結點
Expression<Func<int, int, int, int, int>> lambda = Expression.Lambda<Func<int, int, int, int, int>>(su,a,b,c,d);
Console.WriteLine(lambda + ""); //列印‘(i,j,w,x)=>((i*j)+(w*x))’,z對應參數b,p對應參數a
Func<int, int, int, int, int> f= lambda.Compile(); //将表達式樹描述的lambda表達式,編譯為可執行代碼,并生成該lambda表達式的委托;
Console.WriteLine(f(1, 1, 1, 1) + ""); //列印2
Console.ReadKey();
}