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
 
原文地址:https://www.cnblogs.com/guozhe/p/2388129.html