Lambda 表达式笔记
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 |