MSDN 定義:"Lambda 運算式" 是一個匿名函數,它可以包含運算式和語句, 並且可用於建立委託或運算式樹狀架構類型.
匿名方法是在2.0中引入,在之後的3.0 及 更高版本中, Lambda運算式取代了匿名方法,作為編寫內聯代碼的首選方式. 因為在之前項目中一直沒用,也沒去瞭解, 所以現在整理一下, 趕緊補上這一課.
首先, 回顧一下匿名函數:
要將代碼塊傳遞為委託參數,那麼唯一的方法就是建立匿名函數, Example
delegate void Fun(string str);
Fun f = delegate(string str){
Console.WriteLine(str);
};
f("hello world");
通過使用匿名方法, 因為不必建立單獨的方法, 所以減少了執行個體化委託所需的系統開銷.
線程也是一個很好的例子:
System.Threading.Thread thd = new System.Threading.Thread
(
delegate()
{
Console.WriteLine("first");
}
);
thd.Start();
OK 現在回到"Lambda 運算式"
所有Lambda 運算式都使用 =>, 該運算子讀為"goes to", 運算子左邊是輸入參數(如果有), 右邊包含運算式或者代碼語句塊. x=>x * x 讀作:"x goes to x times x"
=>運算子與賦值(=)有相同的優先順序,並且是右結合運算子.
1、Lambda 運算式
運算式在右邊的 Lambda運算式稱為"Lambda 運算式' (input parameters)=> expression
當只有一個參數時,左邊輸入參數括弧才是可選的, 兩個或更多輸入參數, 則必須有括弧, 參數用逗號分隔
(x,y)=> x==y
有時,編譯器無法推斷輸入類型, 你可以顯式指定參數類型:
(int x, string s) => s.Length > x
使用空括弧指定零個輸入參數:
() => SomeMethod()
2、Lambda 語句
Lambda 語句與 Lambda 運算式類似, 把執行語句塊放到大括弧中 (input
parameters) => {statement;}
Lambda
語句塊可以包含任意數量語句, 但是實際項目中盡量以少為宜, 這樣讀起來比較清晰,一般也就三個語句.
delegate void Fun(string str1,string str2);
Fun f = (n, m) => { Console.WriteLine(n + m + "World"); };
f("Hello", "Our");
3、帶有標準查詢運算子的Lambda
許多標準查詢運算子都具有輸入參數,
其類型是泛型委派Func<T,TResult>系列之一. Lambda 運算式的基礎類型是泛型 Func 委託之一.
Func<T,TResult>委託:封裝一個具有一個參數並返回 TResult 參數指定的類型值的方法。
public delegate TResult Func<in T, out TResult>(
T arg
)
下面就執行個體化一個Func<int,bool> 委託, int 是輸入參數,bool 是傳回值.Example
Func<int, bool> myFun = x => x == 7; or Func<int, bool> myFun = (int x) => x == 7;
bool b = myFun(5); // false
下面顯示一個標準查詢運算子:
int[] numbers = { 3, 4, 5, 6, 7 };
int SumNumbers = numbers.Count(n => n % 2 == 1); //3
這裡Count方法
public static int Count<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate
)
其中source 包含測試和計數的元素的序列. predicate 測試每個元素是否滿足條件的函數
4、Lambda 中的類型推斷
在編寫Lambda時,通常不必為輸入參數指定類型, 編譯器可以根據Lambda 主體、基礎委託類型和C#語言規範描述的其他因素推斷類型.
而大多數標準查詢運算子,第一個輸入是源序列中的元素類型.
customers.Where(c => c.City == "ShangHai"); //這裡的輸入參數 c 就被推斷為customers 對象
Lambda的一般規則:
·Lambda包含的參數數量必須與委託類型包含的參數數量一致
·Lambda中每個輸入參數必須能隱式轉換成其對應的委託參數
·Lambda傳回值必須能夠隱式轉換成委託的傳回型別?
5、Lambda運算式中的變數範圍
先看一段代碼
delegate bool del();
delegate bool del2(int i);
class Program
{
del del;
del2 del2;
static void Main(string[] args)
{
Program test = new Program();
test.TestMethod(5);
bool result = test.del2(10);
Console.WriteLine(result);
Console.Read();
}
public void TestMethod(int input)
{
int j = 0;
del = () => { j = 10; return j > input; };
del2 = (x) => { return x == j; };
Console.WriteLine("j = {0}", j);
bool boolResult = del();
Console.WriteLine("j = {0}.b = {1}", j, boolResult);
}
}
看看輸出結果
MSDN解釋:Lambda 可以引用“外部變數”,這些變數位於在其中定義 Lambda 的封閉方法或類型的範圍內。 將會儲存通過這種方法捕獲的變數以供在 Lambda 運算式中使用,即使變數將以其他方式超出範圍或被作為記憶體回收。 必須明確地分配外部變數,然後才能在 Lambda 運算式中使用該變數。
看起來比較難理解一點, 具體針對下面規則做一些解釋,有理解錯誤的地方還請各位大俠指出 J
?
1)捕獲的變數將不會被作為記憶體回收,直至引用變數的委託超出範圍為止。
Lambda所使用的變數,在委託的生命週期內不會被記憶體回收. 要讓委託承載的方法數超出範圍估計很難達到,
這個具體委託能承載多少個方法,我也想知道, 我試了載入100W個方法,也沒出現問題, 這個應該和記憶體有關, 只要你機器有足夠的記憶體.
2)在外部方法中看不到 Lambda 運算式內引入的變數。
delegate bool D();
D del;
for (int a = 0; a < 4; a++)
{
del += () => { int t = 0; return true; };
Console.WriteLine("t={0}", t); //這裡無法訪問Lambda中定義的變數t
}
3)Lambda 運算式無法從封閉方法中直接捕獲 ref 或 out 參數。
如果委託中有ref或out參數,則Lambda必須有參數類型(即顯示類型)
delegate bool D2(int i,ref string str);
D2 del2;
for (int a = 0; a < 4; a++)
{
del2 += (int x, ref string c) =>
{ c = c + "j"; Console.WriteLine(x + c); return true; };//這裡必須顯示參數類型 x, c}
string s="sadf";
del2(5,ref s);
Console.WriteLine(s);
4)Lambda 運算式中的返回語句不會導致封閉方法返回。
Lambda運算式中return 只是跳出Lambda, 然後繼續執行方法中Lambda下面的語句, 不會跳出當前所在的方法.
5)Lambda 運算式不能包含其目標位於所包含匿名函數主體外部或內部的 goto 語句、break 語句或 continue 語句。
del += () => { int t = 0; break; return true; };
//這裡會出現如下語法錯誤 Control cannot leave the body of an anonymous method or lambda expression |