• 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
     
  • 相关阅读:
    让Controller支持对平铺参数执行@Valid数据校验
    @Validated和@Valid的区别?校验级联属性(内部类)
    Apache和Spring提供的StopWatch执行时间监视器
    Spring方法级别数据校验:@Validated + MethodValidationPostProcessor
    疑问
    第20章 链接详解(笔记)
    nm命令介绍
    使用Euclid算法求最大公约数
    Linux Man手册的使用示例
    VMware12 + Ubuntu16.04 虚拟磁盘扩容
  • 原文地址:https://www.cnblogs.com/guozhe/p/2388129.html
Copyright © 2020-2023  润新知